【题解】Luogu P5279 [ZJOI2019]麻将

原题传送门

希望这题不会让你对麻将的热爱消失殆尽

我们珂以统计每种牌出现的次数,不需要统计是第几张牌

判一副牌能不能和,类似这道题

对于这题:

\(f[i][j][k][0/1]\)表示前\(i\)种牌,顺子\((i-1,i,i+1)\)出现了\(j\)次,顺子\((i,i+1,i+2)\)出现了\(k\)次,有/没有雀头的最多面子数。转移比较简单

我们珂以发现\(j\)这维不太重要,强制dp值不超过\(4\)(超过\(4\)也没有用),雀头数不超过\(7\)(类似),爆搜珂以搜出本质不同的状态一共有\(2091\)

珂以在每个状态珂以在后面加\(x \in [0,4]\)张点数+1的牌,这珂以构成一个自动机,我们叫她和牌自动机

我们每得到一个状态,珂以在和牌自动机上走,判断是否能和

我们设\(dp[i][j][k]\)表示看到前\(i\)种牌,在和牌自动机上的\(j\)状态,已经摸了\(k\)张牌,不胡的种类数,最后算一下期望就珂以了

我们珂以用滚动数组把\(i\)滚掉优化空间

#include <bits/stdc++.h>
#define mod 998244353
#define N 405
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register 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;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline int Min(register int a,register int b)
{
    return a<b?a:b;
}
inline int Max(register int a,register int b)
{
    return a>b?a:b;
}
struct node{
    int f[3][3][2],cnt;
    inline void init()
    {
        memset(f,-1,sizeof(f));
        f[0][0][0]=cnt=0;
    }
    inline int hu()
    {
        if(cnt>=7)
            return 1;
        for(register int i=0;i<3;++i)
            for(register int j=0;j<3;++j)
                if(f[i][j][1]>=4)
                    return 1;
        return 0;
    }
}rt,S[2100];
bool operator < (node a,node b){
    if(a.cnt!=b.cnt)
        return a.cnt<b.cnt;
    for(register int i=0;i<3;++i)
        for(register int j=0;j<3;++j)
            for(register int k=0;k<2;++k)
                if(a.f[i][j][k]!=b.f[i][j][k])
                    return a.f[i][j][k]<b.f[i][j][k];
    return 0;
}
int tot=0;
map<node,int> ma;
inline node trans(register node u,register int w)
{
    node v;
    v.init();
    v.cnt=Min(u.cnt+(w>=2),7);
    for(register int i=0;i<3;++i)
        for(register int j=0;j<3;++j)
        {
            if(~u.f[i][j][0])
            {
                for(register int k=0;k<3&&i+j+k<=w;++k)	
                    v.f[j][k][0]=Max(v.f[j][k][0],Min(u.f[i][j][0]+i+(w-i-j-k>=3),4));
                if(w>=2)
                    for(register int k=0;k<3&&i+j+k<=w-2;++k)
                        v.f[j][k][1]=Max(v.f[j][k][1],Min(u.f[i][j][0]+i,4));
            }
            if(~u.f[i][j][1])
                for(register int k=0;k<3&&i+j+k<=w;++k)
                    v.f[j][k][1]=Max(v.f[j][k][1],Min(u.f[i][j][1]+i+(w-i-j-k>=3),4));
        }
    return v;
}
inline void build(register node u)
{
    if(u.hu())
        return;
    if(ma.find(u)!=ma.end())
        return;
    ma[u]=++tot;
    S[tot]=u;
    for(register int i=0;i<=4;++i)
        build(trans(u,i));
}
int n,s[N],ans;
int fac[N],inv[N],invf[N],tr[2100][5],f[2][2100][N];
inline int C(register int n,register int m)
{
    return 1ll*fac[n]*invf[m]%mod*invf[n-m]%mod;
}
int main()
{
    rt.init();
    build(rt);
    invf[0]=inv[0]=inv[1]=fac[0]=1;
    for(register int i=1;i<N;++i)
        fac[i]=1ll*fac[i-1]*i%mod;
    for(register int i=2;i<N;++i)
        inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    for(register int i=1;i<N;++i)
        invf[i]=1ll*invf[i-1]*inv[i]%mod;
    n=read();
    for(register int i=1;i<=13;++i)
        ++s[read()],read();
    for(register int i=1;i<=tot;++i)
        for(register int j=0;j<=4;++j)
            tr[i][j]=ma[trans(S[i],j)];
    f[0][1][0]=1;
    for(register int i=1,sum=0;i<=n;++i)
    {
        int now=i&1,pre=now^1;
        memset(f[now],0,sizeof(f[now]));
        for(register int j=1;j<=tot;++j)
            for(register int k=s[i];k<=4;++k)
            {
                if(!tr[j][k])
                    continue;
                int w=1ll*C(4-s[i],k-s[i])*fac[k-s[i]]%mod;
                for(register int l=0;l<=4*n-k;++l)
                {
                    if(!f[pre][j][l])
                        continue;
                    f[now][tr[j][k]][l+k]=(0ll+f[now][tr[j][k]][l+k]+1ll*f[pre][j][l]*w%mod*C(k+l-sum-s[i],k-s[i])%mod)%mod;
                }
            }
        sum+=s[i];
    }
    for(register int i=13,w=1;i<=4*n;++i)
    {
        int now=0;
        for(register int j=1;j<=tot;++j)
            now=(0ll+now+f[n&1][j][i])%mod;
        ans=(0ll+ans+1ll*now*w%mod)%mod;
        w=1ll*w*inv[4*n-i]%mod;
    }
    write(ans);
    return 0;
}
posted @ 2019-06-09 15:00  JSOI爆零珂学家yzhang  阅读(342)  评论(0编辑  收藏  举报