题意:给出一段数列 s,只包含 0、1、2 三种数。
每秒:
在每个 2 后面会插入一个 1 ,
在每个 1 后面会插入一个 0,
之后第一个数字消失。
求最后为空串需要多少秒。
思路:
先放上结论:
(1)如果在消除一个 0 前经过了 n 秒,那么消掉这个 0 需要 n + 1 秒。
(2)如果在消除一个 1 前经过了 n 秒,那么消掉这个 1 与其产生的所有数需要 (n + 1) * 2 秒。
(3)如果在消除一个 2 前经过了 n 秒,那么消掉这个 2 与其产生的所有数需要 (2 ^ (n + 1) - 1) * 3 秒。
这结论一开始我是不懂的,发现大佬们都这么写,大概是我太菜了,自己推一边吧。//原来是通过暴力打表得出的??? 想推一边的我放弃了。
//打表代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
string s,h;
long long num;
while(cin>>s)
{
num=0;
while(!s.empty())
{
h="";
for(int i=0;i<s.length();i++)
{
h=h+s[i];
/// cout<<h<<"h"<<endl;
if(s[i]=='2')
{
h=h+'1';
}
else if(s[i]=='1')
{
h=h+'0';
}
}
h=h.substr(1,h.length());
s=h;
cout<<s<<endl;
num++;
}
cout<<num<<endl;
}
}
通过暴力打表即可得上述规律,于是再进行下一步。
由于计算量太大,需要用到取模运算,对于0和1公式都只存在加和乘运算,对结果产生不了影响,可是对于2的时候存在求的运算,然后以该结果作为后面运算的t来运算,模运算的结果发生了变化,若是不会发生变化,都知道是用快速幂来计算,可是为了消除幂运算后产生的变化,我们需要通过计算mod的欧拉值phi,计算了几次就需要取几次mod的欧拉值,即phi(phi(mod))...
欧拉公式:当gcd(a,b)=1时,
当a=2时候,进行推广:
当k>=0时:
emmm,敲这个公式累死了
对k进行操作,取模
可以先处理前面小的那一部分,后面的直接进行递归求解
//膜一下大佬代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
int T,ps[N],x[N],y[N],px[N],pt[N];
char s[N];
int Pow(int x,int y,int mod){
int ans=1;
for (;y;y>>=1,x=1LL*x*x%mod)
if (y&1)
ans=1LL*ans*x%mod;
return ans;
}
int phi(int x){
int ans=x;
for (int i=2;i*i<=x;i++){
if (x%i==0){
ans=ans/i*(i-1);
while (x%i==0)
x/=i;
}
}
if (x>1)
ans=ans/x*(x-1);
return ans;
}
int solve(int j,int k){
if (j==0||pt[j]>0)
return pt[j];
int i=j;
while (i>0&&s[i]!='2')
i--;
int t=i==0?0:((6LL*Pow(2,solve(i-1,k+1),x[k])-3)%x[k]);
for (int p=i+1;p<=j;p++){
if (s[p]=='0')
t=(t+1)%x[k];
if (s[p]=='1')
t=(2*t+2)%x[k];
}
t=((t-y[k])%x[k]+x[k])%x[k];
return t+y[k];
}
int main(){
ps[0]=1e9+7;
for (int i=1;i<=100000;i++)
ps[i]=phi(ps[i-1]);
for (int i=1;i<=100000;i++){
for (x[i]=ps[i-1],y[i]=0;x[i]%2==0;x[i]>>=1,y[i]++);
x[i]=phi(x[i]);
}
x[0]=ps[0],y[0]=0;
scanf("%d",&T);
while (T--){
scanf("%s",s+1);
int n=strlen(s+1),i=0;
for (int x=0;i<n;i++,pt[i]=x){
if (s[i+1]=='0')
x++;
if (s[i+1]=='1')
x=x*2+2;
if (s[i+1]=='2')
x=6*Pow(2,x,1e9+7)-3;
if (x>=21)
break;
}
printf("%d\n",(solve(n,0))%ps[0]);
for (;i>0;i--)
pt[i]=0;
}
return 0;
}