Description
用1×2的 砖头铺满N*M的区域,不能有重叠,一共有多少种方案?
Input
一行输入N和M
Output
输出方案数mod (10^9+7)的值
Sample Input
2 2
Sample Output
2
Data Constraint
50%的数据满足1<=N<=100,1<=M<=11
另外50%的数据满足1<=N<=10^200,1<=M<=5
Solution
先考虑前50%,设状压dp
f[i,j]表示到第i列,第i列的状态为j的方案数
j为二进制状态,为0表示这个位置被左边平放凸出来的块填满或竖着填满,1表示放横着的且凸到了右边去。
预处理两个状态能否转移,即可
100%:矩阵乘法
Code
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define cl(a) memset(a,0,sizeof(a))
#define mo 1000000007
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
ll a[2048][2048],f[201][2048],b[101][101],c[101][101],n[201],q=0,nn;
int m,m1;
ll hzjsb(int k)
{
int j=0;
for(int i=1;i<=m1;i*=2)
{
if((k&i)==0) j++;
else
{
if(j%2==1) return 0;
j=0;
}
}
if(j%2==1) return 0;else return 1;
}
int chu()
{
n[0]=0;
fd(i,q,1)
{
n[i-1]+=(n[i]%2)*10;n[i]/=2;
}
if(n[q]==0) q--;
if(n[0]!=0) return 1;else return 0;
}
void mi()
{
int e[700],l=0;
for(;q>1||n[1]>1;)e[++l]=chu();
for(;l>0;l--)
{
cl(c);
fo(i,0,m1) fo(j,0,m1) fo(k,0,m1) c[i][k]=(c[i][k]+b[i][j]*b[j][k])%mo;
cl(b);
if(e[l]==1)
{
fo(i,0,m1) fo(j,0,m1) fo(k,0,m1) b[i][k]=(b[i][k]+c[i][j]*a[j][k])%mo;
}
else
{
fo(i,0,m1) fo(j,0,m1) b[i][j]=c[i][j];
}
}
}
int main()
{
char ch;
scanf("%c",&ch);
while(ch!=' ')
{
n[++q]=ch-48;scanf("%c",&ch);
}
scanf("%d",&m);
fo(i,1,q/2) swap(n[i],n[q-i+1]);
if(q<=3) fd(i,q,1) nn=nn*10+n[i];
n[1]--;for(int i=1;n[i]<0;i++) n[i]=9,n[i+1]--;if(n[q]==0) q--;
m1=(1<<m)-1;
fo(i,0,m1)
fo(j,0,m1)
if((i&j)==0) a[i][j]=hzjsb(i|j);
fo(j,0,m1) f[0][j]=hzjsb(j);
if(m<=5)
{
fo(i,0,m1) fo(j,0,m1) b[i][j]=a[i][j];
mi();
cl(c);
fo(j,0,m1) fo(k,0,m1) c[0][k]=(c[0][k]+f[0][j]*b[j][k])%mo;
printf("%lld\n",c[0][0]%mo);
}
else
{
fo(i,0,nn-2)
{
fo(j,0,m1)
if(f[i][j])
{
fo(k,0,m1)
if(a[j][k])
{
f[i+1][k]=(f[i+1][k]+f[i][j])%mo;
}
}
}
printf("%lld\n",f[nn-1][0]);
}
}