[SNOI2019]纸牌

传送门

Description

有一副纸牌。牌一共有\(n\)种,分别标有 \(1,2,...,n\),每种有\(C\)张。故这副牌共有\(nC\)张。

三张连号的牌(\(i,i+1,i+2\))或三张相同的牌 \((i,i,i)\) 可以组成一叠。如果一组牌可以分成若干(包括零)叠,就称其为一组王牌

你从牌堆中摸了一些初始牌。现在你想再挑出一些牌组成一组王牌,请问有多少种可能组成的王牌呢?答案对\(998244353\)取模。

两组牌相同当且仅当它们含有的每一种牌数量都相同。

Solution

发现这道题中的\(n\)很大,所以考虑矩阵乘法

发现\((i,i+1,i+2)\)这样的牌,对于相同的\(i\),最多只有两叠

所以考虑一个\(3*3\)的状态表示当前数\(i\)\(i-1\)开始的顺子的数量\((0,1,2)\)

考虑如何转化到下一个数,发现需要枚举\(i+1\)开始的顺子数,将其乘上可行的\((i+1,i+1,i+1)\)的数量的个数

将原题的\(dp\)转化位矩阵乘法

对于已经有初始牌的数,我们考虑单独拿出来转移

可以先把转移矩阵的次幂算出来,减小常数


Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define reg register
#define int ll
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int P=998244353,MX=1005;
ll n,C,X,A[MX],K[MX];
int Mul(int x,int y){return (1LL*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
struct Matrix
{
    int a[9][9];
    Matrix(){memset(a,0,sizeof a);}
    void emp(){for(int i=0;i<9;++i)a[i][i]=1;}
    Matrix operator *(Matrix b)
    {
        Matrix c;
        register int i,j,k;
        for(k=0;k<9;++k)for(i=0;i<9;++i)for(j=0;j<9;++j)
        c.a[i][j]=Add(c.a[i][j],Mul(b.a[i][k],a[k][j]));
        return c;
    }
}tmp,opt[70],ans;
Matrix OPT(int m)
{
    Matrix r;int x=0;r.emp();
    for(;m;m>>=1,++x)if(m&1)r=r*opt[x];
    return r;
}
signed main()
{
    n=read();C=read();X=read();
    register int i,j,k,l;
    for(i=1;i<=X;++i) K[i]=read(),A[i]=read();
    for(i=0;i<3;++i)for(j=0;j<3;++j)for(k=0;k<3;++k)
        if(i+j+k<=C)opt[0].a[j*3+k][i*3+j]=1+(C-i-j-k)/3;
    for(i=1;i<70;++i) opt[i]=opt[i-1]*opt[i-1];
    ans.a[0][0]=1;
    for(l=1;l<=X;++l)
    {
        ans=ans*OPT(K[l]-K[l-1]-1);
        memset(tmp.a,0,sizeof tmp.a);
        for(i=0;i<3;++i)for(j=0;j<3;++j)for(k=0;k<3;++k)
        {
            int least=i+j+k;
            if(least<A[l]) least=A[l]+((least-A[l])%3+3)%3;
            if(least<=C) tmp.a[j*3+k][i*3+j]=1+(C-least)/3;
        }
        ans=ans*tmp;
    }
    ans=ans*OPT(n-K[X]);
    printf("%lld\n",ans.a[0][0]);
    return 0;
}



Blog来自PaperCloud,未经允许,请勿转载,TKS!

猜你喜欢

转载自www.cnblogs.com/PaperCloud/p/11221366.html