题意:
我们定义TM序列为如下形式的布尔序列:
- $T_0 = 0$;
- $T_{2n}=T_n$;
- $T_{2n+1}=1-T_n$。
TM序列是一个无限长度的序列,它有很多连续子序列。
现在给定一个布尔序列S和一个非负整数k,请统计一下一共有多少种TM序列的连续子序列T满足:
- S是T的前缀;
- T是由S额外在右侧添加了恰好k项形成的。
$数据组数Tleq 100,|S|leq 100,kleq 10^{18}$。
题解:
神仙诈骗题。
该序列的生成方式有无数种,我一开始想的是每次取反拼在后面,但好像没什么操作空间。
注意到k的范围是$10^{18}$,这个数据范围要么是$O(1)$要么是$O(log{k})$。
发现自己不会$O(1)$,于是考虑$O(log{k})$,也就是每次把长度压缩成原来的一半。
考虑该序列的另外一个生成方式:每次将0替换成01,将1替换成10。我们反着做这个操作即可压缩序列。
虽然这k个空填啥都行,但仔细想一下,缩的过程中它们都会跟某个确定的元素配对一次,实际上没有累加贡献。
注意每次缩的时候都有两种情况:把第一个元素跟前面/后面配对,在$nleq 3$的时候还得特判一下。
复杂度$O(log{k})$,因为每个串最后只能缩成一个串。
套路:
- 没思路$ ightarrow$根据数据范围猜做法。
- 想了一会还是没思路$ ightarrow$直接写个优秀的暴力走人。
代码:
#include<bits/stdc++.h> #define maxn 100005 #define maxm 500005 #define inf 0x7fffffff #define mod 1000000009 #define ll long long #define mp make_pair #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; map<pair<string,ll>,ll> dp; string S; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline ll solve(string s,ll k){ if(dp[mp(s,k)]) return dp[mp(s,k)]; ll n=s.length(),res=0; if(n==1 && k<=2) return dp[mp(s,k)]=k+1; if(n==2 && k==0) return dp[mp(s,k)]=1; if(n==2 && k==1) return dp[mp(s,k)]=(s[0]==s[1])?1:2; if(n==3 && k==0) return dp[mp(s,k)]=(s[0]!=s[1]||s[1]!=s[2]); string ns; bool flag=1; for(rint i=0;i<n;i+=2){ if(i==n-1 || s[i]!=s[i+1]) ns+=s[i]; else{flag=0;break;} } if(flag) res=(res+solve(ns,(n&1)?(k>>1):((k+1)>>1)))%mod; flag=1,ns=((s[0]-'0')^1)+'0'; for(rint i=1;i<n;i+=2){ if(i==n-1 || s[i]!=s[i+1]) ns+=s[i]; else{flag=0;break;} } if(flag) res=(res+solve(ns,(n&1)?((k+1)>>1):(k>>1)))%mod; return dp[mp(s,k)]=res; } int main(){ ll T=read(); while(T--){ dp.clear(); cin>>S; ll k=read(); printf("%d ",solve(S,k)); } return 0; }