bzoj 3120 Line 矩乘 dp

2 篇文章 0 订阅

每行结尾连续的1的个数只能是0,1,2。
状态记录一下有几行结尾连续的1的个数为0,几行结尾连续的1的个数为1,当前有多少列为全1。
然后矩乘转移就好了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000007
int n,P,Q,cnt,ans;
ll m;
struct Matrix
{
    int w[185][185];
    void init()
    {
        memset(w,0,sizeof(w));
        for(int i=1;i<=cnt;i++)w[i][i]=1;
    }
}mat,tmp;
void mul(Matrix &a,Matrix &b,Matrix &c)
{
    memset(&tmp,0,sizeof(tmp));
    for(int i=1;i<=cnt;i++)
        for(int k=1;k<=cnt;k++)if(a.w[i][k])
            for(int j=1;j<=cnt;j++)if(b.w[k][j])
                tmp.w[i][j]=(tmp.w[i][j]+(ll)a.w[i][k]*b.w[k][j])%mod;
    c=tmp;
}
int num[5][11][11],C[11][11];
Matrix qpow(Matrix x,ll y)
{
    Matrix ret;ret.init();
    while(y)
    {
        if(y&1)mul(ret,x,ret);
        mul(x,x,x);y>>=1;
    }
    return ret;
}
int main()
{
    scanf("%d%lld%d%d",&n,&m,&P,&Q);
    if(P==1)return puts("1"),0;
    for(int i=0;i<=n;i++)C[i][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];

    for(int i=0;i<=Q;i++)
        for(int j=0;j<=n;j++)
            for(int k=0;j+k<=n;k++)
                if(P!=2||j+k==n)
                    num[i][j][k]=++cnt;
    for(int i=0;i<=Q;i++)
        for(int a1=0;a1<=n;a1++)
            for(int b1=0;b1+a1<=n;b1++)
                if(P!=2||a1+b1==n)
                {
                    int t1=num[i][a1][b1];
                    for(int a2=0;a2<=a1;a2++)
                        for(int b2=0;b2<=b1;b2++)
                            if(P!=2||b2==0)
                            {
                                int t2=num[i+(a2+b2==n)][(n-a1-b1)+(a1-a2)+(b1-b2)][a2];
                                if(i+(a2+b2==n)>Q)continue;
                                (mat.w[t1][t2]+=C[a1][a2]*C[b1][b2])%=mod;
                            }
                }
    mat=qpow(mat,m);
    int t=num[0][n][0];
    for(int i=1;i<=cnt;i++)
        ans=(ans+mat.w[t][i])%mod;
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值