传送门
SDOI今年怎么有三道数数题。。。
题解:
仔细一看发现是 T h u e − M o r s e \mathrm{Thue-Morse} Thue−Morse序列,我就知道肯定没有什么好事。。。
我不知道在多少道找规律题里面发现过这个序列了,然而没有一次是做出来了的。。。
根据题目的提示,我们考虑这样构造:对于当前串,将 0 0 0替换成 01 01 01并将 1 1 1替换成 10 10 10,于是考虑缩原来的那个串,每次我们可以把问题规模缩小一半。
缩串需要考虑两种方式,直接从头开始两两匹配,或者在串前加一个字符然后两两匹配。
注意到长度大于 3 3 3的串会有唯一的合法缩串的方案,证明的话就是考虑其中一个方案出现了 00 00 00或者 11 11 11,另一个方案就一定会出现 000 000 000或者 111 111 111。
然后暴力dfs,并且在串长小于等于3的时候特判就行了。
我们需要处理一个字符后面接上某个长度后是合法子序列的方案数,容易发现 f ( k ) = f ( ⌊ k 2 ⌋ ) + f ( ⌊ k + 1 2 ⌋ ) f(k)=f(\lfloor\frac{k}{2}\rfloor)+f(\lfloor\frac{k+1}{2}\rfloor) f(k)=f(⌊2k⌋)+f(⌊2k+1⌋),记忆化一下,然后在 k ≤ 3 k\leq 3 k≤3的时候也要特判。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int mod=1e9+9;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
std::unordered_map<ll,int> ma;
inline int get(ll k){
if(ma.count(k))return ma[k];
switch(k){
case 0:return 1;
case 1:return 2;
case 2:return 3;
}return ma[k]=add(get(k>>1),get(k+1>>1));
}
inline bool shrink(cs std::string &s,std::string &ns){
ns.clear();
for(int re i=0;i<s.size();i+=2)
if(s[i]=='0'&&(i+1==s.size()||s[i+1]=='1'))ns+="0";
else if(s[i]=='1'&&(i+1==s.size()||s[i+1]=='0'))ns+="1";
else return false;
return true;
}
inline int calc(cs std::string &s,ll k){
if(s.size()==1)return get(k);
if(s.size()==2&&k==0)return 1;
if(s.size()==2&&k==1)return s[0]==s[1]?1:2;
if(s.size()==3&&k==0)return s[0]!=s[1]||s[1]!=s[2];
std::string ns;int ans=0;
if(shrink(s,ns))Inc(ans,calc(ns,k+!(s.size()&1)>>1));
if(shrink(std::string(s[0]=='0'?"1":"0")+s,ns))Inc(ans,calc(ns,k+(s.size()&1)>>1));
return ans;
}
signed main(){
#ifdef zxyoi
freopen("sequence.in","r",stdin);
#endif
std::ios::sync_with_stdio(false);
int T;std::cin>>T;std::string s;ll k;
while(T--)std::cin>>s>>k,std::cout<<calc(s,k)<<"\n";
return 0;
}