题目链接:http://poj.org/problem?id=3420
题目大意:一个4*n的矩阵,用2*1的骨牌覆盖,有多少种方法mod M【n<=1e9 m<=1e5】
看到网上很多人的题解 都是构造一个16*16的矩阵。。
有点没想通为什么。。
我构造的是一个4*4的矩阵。
首先想好如何转移。。
假如当前计算到了dp[n]。。
首先可以由dp[n-1]转移来,方法只有1种。。
然后是dp[n-2],方法有4种。。
之后是dp[n-3]之前的状态。。
假如一个状态和n的距离为奇数,就只有2种转移方法。。
如果是偶数 就是3种转移方法。。
设sumodd为n-3之前 为奇数的状态个数和
sumevev为n-3之前 为哦偶数的状态个数和
总体的转移方程是
dp[n]=dp[n-1]+4*dp[n-2]+2*sumeven[n-3]+3*sumodd[n-3]; 【n为奇数】
dp[n]=dp[n-1]+4*dp[n-2]+3*sumeven[n-3]+2*sumodd[n-3];【n为偶数】
初始状态 dp[1]=1 dp[2]=5;
因为一次要转移出一奇一偶两个状态 我们要推出dp[n+1]的公式
我们始终认定 n为 奇数
dp[n+1]=dp[n]+4*dp[n-1]+3*sumeven[n-2]+2*sumodd[n-2]
=dp[n-1]+4*dp[n-2]+2*sumeven[n-3]+3*sumodd[n-3]+4*dp[n-1]+3*sumeven[n-2]+2*sumodd[n-2]
因为n为奇数 sumeven[n-2]==sumeven[n-3] sumodd[n-2]==sumodd[n-3]+dp[n-2]
=5*dp[n-1]+6*dp[n-2]+5*sumeven[n-3]+5*sumodd[n-3];
从这个转移方程就可以构造一个矩阵。。
| dp[n+1] | | dp[n-1] | | 5 1 1 0 |
| dp[n] | | dp[n-2] | | 6 4 0 1 |
| sumeven[n-1] | ===== | sumeven[n-3] | * | 5 2 1 0 |
| sumodd[n-1] | | sumodd[n-3] | | 5 3 0 1 |
最后看n是奇数还是偶数 决定输出第一项还是第二项
#include<set> #include<cmath> #include<stack> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cstdlib> #include<numeric> #include<vector> #include<ctime> #include<queue> #include<list> #include<map> #define pi acos(-1) #define INF 0x7fffffff #define clr(x) memset(x,0,sizeof(x)); #define clrto(x,siz,y) for(int xx=0;xx<=siz;xx++) x[xx]=y; #define clrset(x,siz) for(int xx=0;xx<=siz;xx++) x[xx]=xx; #define clrvec(x,siz) for(int xx=0;x<=siz;xx++) x[xx].clear(); #define fop freopen("in.txt","r",stdin);freopen("out.txt","w",stdout); #define myprogram By_135678942570 #define clrcpy(x,siz,y) for(int xx=0;xx<siz;xx++) x[xx]=y[xx]; using namespace std; main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0) break; long long maz[4][4]={5,1,1,0,6,4,0,1,5,2,1,0,5,3,0,1}; long long int num[4]={5,1,1,0}; int flag=n%2; if(n==1) printf("%d\n",1%m); else if(n==2) printf("%d\n",5%m); else { n-=2; n=n+1>>1; while(n) { if(n&1) { long long temp[4]={0}; for(int i=0;i<4;i++) for(int j=0;j<4;j++) temp[i]=(temp[i]+num[j]*maz[j][i])%m; for(int i=0;i<4;i++) num[i]=temp[i]; } long long temp[4][4]={0}; for(int i=0;i<4;i++) for(int j=0;j<4;j++) for(int k=0;k<4;k++) temp[i][j]=(temp[i][j]+maz[i][k]*maz[k][j])%m; for(int i=0;i<4;i++) for(int j=0;j<4;j++) maz[i][j]=temp[i][j]; n>>=1; } if(flag==0) printf("%d\n",num[0]); else printf("%d\n",num[1]); } } return 0; }