【GDOI模拟】暴走的图灵机
2016-04-09 23:53
309 查看
Description
你有l和r两个字符串,初始l=”0”,r=”1”,每轮操作定义如下。将r变成原来的l+原来的r,这里的+表示将两个字符串连接起来
将l变成原来的l
给定一个长度为m的匹配串s,问操作n次后,l中包含了多少个s作为子串,答案对p取模。
n≤109,m≤104,0<p≤109
Solution
首先这道题目就是一个斐波拉契形式的字符串组合。刚做这道题的时候,找到了一个字符串的规律,以为这是具有普遍性的,然后打了个矩阵……后来发现错了,最后还是打了个暴搜30分。
其实。当时可以进一步的思考。
首先我们可以发现这道题目的m并不是很大。
然后字符串加入的规则是上一个的r+l那么匹配串的个数也是r的答案加上l的答案,再加上因为拼接而产生的答案。然而我们只需要把r的后m-1个和l的前m-1个加起来,做一次KMP就好了。
再考虑m,我们发现当串的长度比2m-2大时头和尾基本就不变了,那么中间贡献的答案在模p意义下是有循环节的,可以发现这个循环节为2。
然后建立矩阵,矩阵快速幂就好了。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define fo(i,a,b) for(i=a;i<=b;i++) typedef long long ll; using namespace std; int i,j,k,l,t,n,m,p,r,next[30000],c[1000007],tot,ans1[1000007]; ll ans[1000007]; char s[40][1000007],st[1000007],sr[1000007]; struct node{ ll ju[4][4]; node friend operator *(node x,node y){ int i,j,k; node z; memset(z.ju,0,sizeof(z.ju)); fo(i,0,3){ fo(j,0,3){ fo(k,0,3){ z.ju[i][j]=(x.ju[i][k]*y.ju[k][j]%p+z.ju[i][j])%p; } } } return z; } }a,b; node qsm(node x,int y){ node z; memset(z.ju,0,sizeof(z.ju)); int i; fo(i,0,3)z.ju[i][i]=1; while(y!=0){ if(y&1!=0)z=z*x; x=x*x; y/=2; } return z; } int main(){ scanf("%d%d%d",&n,&m,&p); scanf("%s",st+1); fo(i,2,m){ while(j&&st[i]!=st[j+1])j=next[j]; if(st[i]==st[j+1])j++; next[i]=j; } s[0][1]='0';s[1][1]='1';c[0]=c[1]=1; fo(i,2,n){ fo(j,1,c[i-2])s[i][++c[i]]=s[i-2][j]; fo(j,1,c[i-1])s[i][++c[i]]=s[i-1][j]; tot=0; int u=min(m-1,c[i-2]),v=min(m-1,c[i-1]); fo(j,1,u)sr[++tot]=s[i-2][c[i-2]-u+j]; fo(j,1,v)sr[++tot]=s[i-1][j]; k=t=0; fo(j,1,tot){ while(k&&sr[j]!=st[k+1])k=next[k]; if(sr[j]==st[k+1])k++; if(k==m)ans1[i]=(ans1[i]+1)%p,k=next[k]; } if(c[i-1]>2*m)break; } t=i; if(m==1){ if(st[1]=='0')ans[0]=1;else ans[1]=1; } fo(i,2,t){ ans[i]=(ans[i-2]+ans[i-1]+ans1[i])%p; } if(t==n){ printf("%lld\n",ans ); return 0; } b.ju[0][0]=1;b.ju[0][1]=1;b.ju[0][2]=0;b.ju[0][3]=0; b.ju[1][0]=1;b.ju[1][1]=2;b.ju[1][2]=0;b.ju[1][3]=0; b.ju[2][0]=1;b.ju[2][1]=1;b.ju[2][2]=1;b.ju[2][3]=0; b.ju[3][0]=0;b.ju[3][1]=1;b.ju[3][2]=0;b.ju[3][3]=1; memset(a.ju,0,sizeof(a.ju)); a.ju[0][0]=ans[t-1];a.ju[0][1]=ans[t]; a.ju[0][2]=ans1[t-1];a.ju[0][3]=ans1[t]; l=n-t; b=qsm(b,l/2); a=a*b; if(l%2){ printf("%lld\n",(a.ju[0][0]+a.ju[0][1]+a.ju[0][2])%p); } else{ printf("%lld\n",a.ju[0][1]); } }
相关文章推荐
- 异步任务加载网络数据——AsyncTask使用
- Android最佳实践之Material Design
- 京东2016实习生在线编程题
- KNN算法的感受 1
- Linux内核如何装载和启动一个可执行程序-----实验7
- 第七十八天
- linux中清除cache的方法
- LeetCode 209. Minimum Size Subarray Sum
- 使用.NET统计文件夹中文件总数
- linux中清除cache的方法
- HDU-3586 Information Disturbing(树形DP+删边)
- return能否跳出循环?终止循环。
- perf学习-linux自带性能分析工具
- perf学习-linux自带性能分析工具
- 【UFLDL-exercise6- Self-Taught Learning】
- Linux系统-目录的创建,移动或重命名和删除
- EventBus StickyEvent(粘性事件) 的应用场景! 不需要手动注销事件总线
- c++程序设计第一章 开始接触c++
- Ubuntu 14.10集成redmine和gitlab(bitnami stacks)
- 栈溢出笔记1.7 地址问题(2)