Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 3422 | Accepted: 1492 |
Description
Tired of the Tri Tiling game finally, Michael turns to a more challengeable game, Quad Tiling:
In how many ways can you tile a 4 × N (1 ≤ N ≤ 109) rectangle with 2 × 1 dominoes? For the answer would be very big, output the answer modulo M (0 < M ≤ 105).
Input
Input consists of several test cases followed by a line containing double 0. Each test case consists of two integers, N and M, respectively.
Output
For each test case, output the answer modules M.
Sample Input
1 10000 3 10000 5 10000 0 0
Sample Output
1 11 95
前序分析见我的另一篇博客,传送门:poj2411题解
这题其实采用的是poj2411的第二种方法,但是利用第二种方法,我们发现行数太多,必然超时。
但是我们观察poj2411每重for做了一件什么事?
对于m列,我们有1<<m个状态,状态之间有转移关系,我们将这些状态看成点,那么这些点之间的关系看成距离为1的路径,那么每重for其实是在原先到达情况下,又多走了距离1,也就是相当于做了k重for表示距离小于等于k的路径条数,这个正好是可达矩阵的k次方,突然吓傻,矩阵快速幂搞起啊!
最后,因为第0行我们假设是全1状态这样可以保证不影响第一行的方法,而最终状态也是全1,也就是本题转化为从点15走到点15,距离小于等于n的路径条数。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#define Maxn 20
using namespace std;
int mod;
int d;
struct matrix{
long long mat[Maxn][Maxn];
matrix(){memset(mat,0,sizeof mat);}
matrix operator*(matrix &x){
matrix ans;
for(int i=0;i<d;i++)
for(int j=0;j<d;j++)
for(int k=0;k<d;k++){
ans.mat[i][j]+=mat[i][k]*x.mat[k][j];
ans.mat[i][j]%=mod;
}
return ans;
}
}x;
matrix quick_pow(matrix x,int k){
matrix ans;
for(int i=0;i<d;i++)
ans.mat[i][i]=1;
while(k){
if(k&1) ans=ans*x;
x=x*x;
k>>=1;
}
return ans;
}
int n,m;
void dfs(int c,int s,int scur){
if(c>4) return;
if(c==4){
x.mat[s][scur]=1;
return;
}
dfs(c+1,s<<1,scur<<1|1);
dfs(c+1,s<<1|1,scur<<1);
dfs(c+2,s<<2|3,scur<<2|3);
}
int main()
{
while(cin>>n>>mod,n){
memset(x.mat,0,sizeof x.mat);
dfs(0,0,0);
d=16;
x=quick_pow(x,n);
cout<<x.mat[15][15]<<endl;
}
return 0;
}