POJ 3420 Quad Tiling DP?递推?+矩阵快速幂

原题链接


题目大意:
有多少种方法可以用2*1的骨牌填满4*n的棋盘


分析:
首先这道题不可能打表,其次它也不可能用STMD算法,它怎么看都像一道数学题,但是我们又不能直接计算,所以我们使用递推的方法
f[i][* * * * ]代表将要填第i行的四个格子时且第i行状态为 * * * * 的方案数,然后我们考虑一共有几种状态
0—满行1111
1—中间空1001
2—两边空0110
3—一侧空0011/1100
4—全空0000
怎么转移呢??
f[i][0]–>f[i+1][0]&f[i+1][4]&f[i+1]3&f[i+1][1]
f[i][1]–>f[i+1][0]&f[i+1][2]
f[i][2]–>f[i+1][1]
f[i][3]–>f[i+1][3]&f[i+1][0]
f[i][4]–>f[i+1][0]
注意,因为3包含两种情况,所以当前状态为空且要填满当前行使得下一行状态为一侧空的方案数要乘2
初值是什么呢??
f[0][0]=1,我们什么都没填,当前行肯定为空,且只有一种方案>_<
那么目标是啥捏??
f[n+1][0]也就是第n+1行为空时(为什么不是第n行为满时呢??因为还有可能是第n行为中间空或一侧空时,考虑起来太麻烦,所以直接考虑填完n行的状态,多么得小清新………)
然后我们就开心地想要去写递推了………..
然后我们就忧伤地发现1 ≤ N ≤ 10^9,……………..QAQ
所以还是考虑考虑矩阵快速幂吧…………..
建一个5*5的矩阵
1 1 0 2 1
1 0 1 0 0
0 1 0 0 0
1 0 0 1 0
1 0 0 0 0
然后转移就好啦…………>_<


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
using namespace std;
int n,MOD;
struct Matrix{
    int a[5][5];
    Matrix(){
        memset(a,0,sizeof(a));
    }
    friend Matrix operator *(Matrix a,Matrix b){
        Matrix ans;
        for(int i=0;i<=4;i++)
            for(int j=0;j<=4;j++)
                for(int k=0;k<=4;k++)
                    ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j]%MOD)%MOD;
        return ans;
    }
}ONE,S,X;
inline int read(){
    char ch=getchar();
    int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return f*x;
}
Matrix power(Matrix a,int b){
    Matrix ans=ONE; 
    while(b){
        if(b&1)
            ans=ans*a;
        a=a*a,b>>=1;
    }
    return ans;
}
signed main(void){
    /*0---满行1111,1---中间空1001,2---两边空0110,3---一侧空0011/1100,4---全空0000 
    f[i][0]-->f[i+1][0]&f[i+1][4]&f[i+1][3](*2)&f[i+1][1] 
    f[i][1]-->f[i+1][0]&f[i+1][2]
    f[i][2]-->f[i+1][1]
    f[i][3]-->f[i+1][3]&f[i+1][0]
    f[i][4]-->f[i+1][0]*/
    S.a[0][0]=S.a[0][1]=1,S.a[0][4]=S.a[1][0]=1,S.a[0][3]=2,S.a[1][2]=S.a[2][1]=S.a[3][0]=1,S.a[3][3]=S.a[4][0]=1;  
    X.a[0][0]=1;
    while(n=read(),MOD=read()){
        for(int i=0;i<=4;i++)
            ONE.a[i][i]=1;
        Matrix ans=X*power(S,n);
        cout<<ans.a[0][0]<<endl;
    }
    return 0;
} 

by >o< neighthorn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值