Jzoj4787 数格子
题意:用1x2的骨牌填满4xN的格子的方案数
设f[i][j]表示填到第i行,这一行的状态为j的方案数
f[i][j]=Σf[i-1][k],其中k->j是一个合法转移
合法转移要满足两个条件:
1.若k的第x位为0,那么j的第x位一定要为1 (~k|j==j)
2.k,j都为一的位置必须是偶数个且必须连续,即k&j的二进制不能出现单独一个1,例如0101不合法,0011和0110都合法
这样就可以转移了,最后可以用矩阵加速
#include<stdio.h>
#include<string.h>
#define L long long
const int w[5]={0,3,6,12,15};
int n,M;
struct Mat{
int n,m;
int s[16][16];
Mat(){ memset(s,0,sizeof s); }
void clr(){ memset(s,0,sizeof s); }
void set(int x,int y){ n=x; m=y; }
Mat operator * (const Mat& b){
Mat c; c.set(n,b.m);
for(int i=0;i<n;++i)
for(int j=0;j<b.m;++j)
for(int k=0;k<m;++k)
c.s[i][j]=(c.s[i][j]+(L)s[i][k]*b.s[k][j])%M;
return c;
}
} a,b;
bool ok(int x){
for(int i=0;i<5;++i) if(x==w[i]) return 1;
return 0;
}
void pow(int k){
for(;k;b=b*b,k>>=1) if(k&1) a=a*b;
}
int main(){
a.set(1,16); b.set(16,16);
begin:
a.clr(); b.clr();
for(int i=0;i<16;++i)
for(int j=0;j<16;++j)
if(((~i&15)|j)==j&&ok(i&j)) b.s[i][j]=1;
a.s[0][15]=1;
scanf("%d%d",&n,&M);
if(n&&M){
pow(n);
printf("%d\n",a.s[0][15]);
goto begin;
}
}