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
",a.s[0][15]);
		goto begin;
	}
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/7774336.html