数学题目。
设数列a[ i ]表示第 i 个位置的正反情况。并以1表示向上,0表示向下。
从反面考虑,即求最长相连的同值a[]数不超过2时,(a)的个数,设之为p[n];
假设a[1]=x,进行分类:
1) a[2]=!x;
有p[n-1]种序列。即对a[2]值的指派不会减少2到n的子(a)个数。
乍看之下这是奇怪的,但仅仅需要认识到以下几点,结论便是显然的。
a.!x的指派是两种而非一种。
b.由于a[1]与a[2]指派不同,在问题的结果中间,任意的从2到n的子(a),在本身合法的条件下,构成的(a)也合法。
2) a[2]=x;
那么,显然的,a[3]=!x;利用(1)中的结论,有p[n-2]种序列(a)。
那么p[n]=p[n-1]+p[n-2];
另外p[1]=2,p[2]=4,故,根据p[]的递推公式,形式上的拓展p[]的定义,使p[0]=2,p[-1]=0;
那么显然的,p[n]=2×f[n+1] (f[n]表示斐波那契数列)。
问题的解就是ans=2^n-2×f[n+1];(n>=3)
ans=0(n<3)
另外,代码的##处利用费马小定理减小了数据规模,节省了时间。不过就本题而言,这个优化也许不是必要的。
#include<stdio.h> #define M 10007 int numqpow(int a,int k) { int ans=1; while(k) { if(k&1)ans*=a; k>>=1; a*=a; ans%=M; a%=M; } return ans; } struct mat { int x[3][3]; }m0;//step=2 mat mul(mat a,mat b) { mat c; for(int i=1;i<=2;i++) { for(int j=1;j<=2;j++) { c.x[i][j]=0; for(int k=1;k<=2;k++) { c.x[i][j]+=a.x[i][k]*b.x[k][j]; c.x[i][j]%=M; } } } return c; } mat matqpow(mat a,int k) { mat c=a; k--; while(k) { if(k&1)c=mul(c,a); k>>=1; a=mul(a,a); } return c; } void init() { m0.x[1][1]=1; m0.x[1][2]=1; m0.x[2][1]=1; m0.x[2][2]=0; } int fib(int n) { mat ans=matqpow(m0,n); return ans.x[1][2]; } int main() { int n; while(scanf("%d",&n)==1) { if(n<3) { printf("0\n"); continue; } init(); int ans=0-2*fib(n+1); //printf("%d\n",ans); n%=(M-1);//## ans+=numqpow(2,n); while(ans<0)ans+=M; ans%=M; printf("%d\n",ans); } return 0; }