题意:现在有无数的1*1*2的砖头,要垒成一个长度为N的烟囱。砖头可以竖起来,可以平着放,问题是当四块砖都平f放时,题目认为可以有两种情况。
做法:用0代表空,1代表覆盖。模拟每个截面放置砖头的情况后,可以发现不管怎么放,1的个数总是偶数,这可以用来化简。由于放置的特殊性,其实截面可以抽象成一个首尾可以相互影响的8位二进制数。如此,考虑所有情况,进行判断。
#include <cstdio> #include<cstring> #define mod 1000000007 #define LMT 130 #define LL long long //~的优先级比位移低 //定义在结构体内就爆内存了.. //少在main中定义 //(x>>i)&1,才是判断啊 //给矩阵乘法跪了 using namespace std; class matrix { public: LL mat[LMT][LMT]; int n,m; void init(void) { memset(mat,0,sizeof mat); } friend matrix operator*(matrix &,matrix &); }; matrix matt,trans; int state[260],lim=1<<8; matrix operator *(matrix &a,matrix &b) { matrix tem; tem.n=a.n;tem.m=b.m; tem.init(); for(int k=0;k<a.m;k++) for(int i=0;i<a.n;i++) if(a.mat[i][k]) for(int j=0;j<b.m;j++) tem.mat[i][j]=(tem.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod; return tem; } bool yes(int code) { int s=0; while(code) { s+=code&1; code>>=1; } return s%2==0; } LL can(int a,int b) { int st,i,j; bool end=false; if(a==0)return b==255? 1LL:0; for(i=0;i<8;i++) { j=(i+7)%8; if((a&(1<<i))&&(a&(1<<j))==0) { st=i; break; } } for(i=st;i!=st||end==false;i=(i+1)%8) { end=true; j=(i+1)%8; if(0==(a&(1<<i))) { if(0==(b&(1<<i)))return 0; } else { if((a&(1<<j))&&(b&(1<<i))&&(b&(1<<j)))i++; else if((b&(1<<i))==0)continue; else return 0; } } return 1LL; } void init(void) { lim=0; memset(state,-1,sizeof(state)); for(int i=0;i<1<<8;i++) if(yes(i))state[i]=lim++; trans.init(); trans.m=trans.n=lim; for(int i=0;i<1<<8;i++) for(int j=i+1;j<1<<8;j++) if(state[i]!=-1&&state[j]!=-1) trans.mat[state[j]][state[i]]=trans.mat[state[i]][state[j]]=can(i,j); trans.mat[lim-1][lim-1]=2; } matrix pow(matrix t,int n) { matrix tem; tem.m=tem.n=lim; tem.init(); for(int i=0;i<lim;i++) tem.mat[i][i]=1; while(n) { if(n&1)tem=tem*t; t=t*t; n>>=1; } return tem; } int main(void) { int T,I,n; init(); scanf("%d",&T); for(I=1;I<=T;I++) { scanf("%d",&n); matt=pow(trans,n); printf("Case %d: %I64d\n",I,matt.mat[lim-1][lim-1]); } return 0; }