转载(看到一篇博客分析不错,就不自己写啦 )
咋一看还以为是DP,当看到规模的时候傻眼了,10^9!!!!!这下咋办…
后来才知道是要推公式的,问了两位大牛,公式是这么推的:
长度为n的01串一共有2^n种不同的排列方法,
设f(n)为长度是n的不包含连续3个或以上相同的1或0的01串,
则f(1)=2,f(2)=4,f(3)=6,f(4)=10
当n>4的时候,分情况考虑:
1、 如果是以00或者11结尾,则分别有f(n-2)/2种情况,加起来就是f(n-2)种.
2、 如果是以01或者10结尾,则第n个字符要和第n-1个字符不一样,那么分别有f(n-1)/2种,加起来就是f(n-1)
则统计起来就是f(n)=f(n-1)+f(n-2),题目要求的是包含连续三个相同的0或1串的串数,那就是用a[n]=(2^n-f(n))007.
然而这样还不好求,先不看007,转换成递推公式是a[n]=a[n-1]+a[n-2]+2^(n-2),
转换成矩阵:
a[n] 1 1 1 a[n-1]
a[n-1] = 1 0 0 * a[n-2]
2^(n-1) 0 0 2 2^(n-2)
这样就可以用矩阵幂快速算出a[n],复杂度为O(logn)
代码原创
#include<cstring> #include<cstdio> #include<iostream> using namespace std; const int N=10007; int x[4][4],ret[4][2],t[4][4]; void f1() { memset(t,0,sizeof(t)); for(int i=1;i<=3;i++) for(int k=1;k<=3;k++) t[i][1]=(t[i][1]+x[i][k]*ret[k][1])%N;; for(int i=1;i<=3;i++) ret[i][1]=t[i][1]; } void f2() { memset(t,0,sizeof(t)); for(int i=1;i<=3;i++) for(int k=1;k<=3;k++) for(int j=1;j<=3;j++) t[i][j]=(t[i][j]+x[i][k]*x[k][j])%N; for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) x[i][j]=t[i][j]; } int main() { int a[5]={0,0,0,2,6}; int m; while(scanf("%d",&m)!=EOF) { if(m<=4){printf("%d\n",a[m]);continue;} m-=4; x[1][1]=x[1][2]=x[1][3]=x[2][1]=1; x[2][2]=x[2][3]=x[3][1]=x[3][2]=0; x[3][3]=2; ret[1][1]=6,ret[2][1]=2;ret[3][1]=8; while(m>0) { if(m&1)f1(); f2(); m=m/2; } printf("%d\n",ret[1][1]); } return 0; }