题目大意
有一个无限长的01串(T)满足:
n=0时,(T_n=0);n为偶数时,(T_n=T_{frac{n}{2}});n为奇数时,(T_n=1-T_{frac{n-1}{2}})。
该串的前几位:01101001100101101001011001101001
多组询问,每次询问给定01串(S)和长度(k),问对于所有在(T)里出现的和(S)一样的子串,它们后面紧接的(k)位有多少种不同的01串。
(询问数leq 100;|S|leq 100;kleq 10^{18};)
题解
T这个01串相当于是:
一开串有个0,然后把0换为01变成01,把0换为01把1换为10变成0110,同样的变换变为01101001,变为0110100110010110。
这样转化有个好处就是可以反着来,可以考虑把(S)串长度减半。
可以把(S)串每相邻两个分一组,按10->1,01->0的规则逆变换;或者在(S)最前面在加一个0或1,在按这个规则逆变换。
发现当分到同一组的存在00或11时,变换就是不合法的。
对于任何一个长度大于等于6的串,它其中只要出现连续三次01交替的,逆变换一次就会出现000或111,怎么分都不合法;如果没有出现连续三次01交替,就一定有1001或0110,它们都只有1中逆变换方法。
对于长度为4或5的串,如果有连续两次01交替,逆变换一次后出现00或11,00或11只有一种逆变换方法;如果没有,同上。
所以长度大于3的串的合法的逆变换方法不超过1中。可以只对长度不超过3的串搜索,并手算出长度为1或2的串的一部分比较好算的答案。
在对(S)进行逆变换时,同时也要对后(k)位逆变换:如果(S[|S]-1])没和(S[|S|])分到一组,为了使逆变换合法,相当于是(S)后的第一位已经确定了,所以(k)的长度逆变换后是(lfloorfrac{k}{2}
floor);反之,长度为(k)的串能分(lceilfrac{k}{2}
ceil)组,(k)逆变换后是(lceilfrac{k}{2}
ceil)。
在经过几次逆变换后(S)的长度会变成1,但因为(k)极大,可能还会再变换几次才变成手算出的那部分的大小。这样时间看上去是(2^{log_2space 10^{18}}=10^{18})级别的。
但是发现(k)递归时会变成(lfloorfrac{k}{2}
floor)或(lceilfrac{k}{2}
ceil),也就是说搜到的不同的(k)的值数量是在(log_2space k)级别的。可以记忆化搜索。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define psl pair<string,LL>
#define LL long long
#define fi first
#define se second
#define mp make_pair
using namespace std;
LL read()
{
LL x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('
');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
return;
}
const int mod=1e9+9;
int t;
LL n;
string s,nxts;
map<psl,int>M;
int mo(int x){x%=mod;if(!x)x=mod;return x;}
int work(psl x)
{
//cout<<x.fi<<" "<<x.se<<endl;
if(x.fi.size()==1){if(x.se<=2)return x.se+1;}
if(x.fi.size()==2)
{
if(x.se==0)return 1;
if(x.se==1)return (x.fi[0]==x.fi[1])?1:2;
}
if(x.fi.size()==3)
{
if(x.fi[0]==x.fi[1]&&x.fi[1]==x.fi[2])return 0;
if(x.se==0)return 1;
}
if(M[x]){return M[x];}
nxts.clear();int li=x.fi.size()-1,no=0,res=0;
for(int i=0;i<=li;i+=2)
{
if(i+1<=li&&x.fi[i]==x.fi[i+1]){no=1;break;}
else nxts+=x.fi[i];
}
if(!no){res=work(mp(nxts,(x.fi.size()&1)?(x.se>>1ll):(x.se+1ll>>1ll)));}
nxts.clear();no=0;
nxts+=(x.fi[0]=='1')?'0':'1';
for(int i=1;i<=li;i+=2)
{
if(i+1<=li&&x.fi[i]==x.fi[i+1]){no=1;break;}
else nxts+=x.fi[i];
}
if(!no){res+=work(mp(nxts,(x.fi.size()&1)?(x.se+1ll>>1ll):(x.se>>1ll)));}
return M[x]=mo(res);
}
int main()
{
t=read();
while(t--)
{
cin>>s;n=read();
write(work(mp(s,n))%mod);
}
return 0;
}
奇怪的思路
(T_i=(i的二进制数位和)modspace 2) 。然而毫无用处。