铺砖问题

55 篇文章 0 订阅
9 篇文章 0 订阅

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]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值