「ZJOI2019」麻将

传送门

Solution 

对于条件一:记录一个\(cnt\)表示牌个数\(≥2\)的个数

\(dp_{i,0/1,j,k}\)表示考虑了\(1...i\),当前是否有对子,以\(i-1\),\(i\)开始的顺子数为\(j\),\(k\)个,的最大面子数

其中\(dp_i\)是一个大小为\(18\)的状态,我们通过搜索可以发现这样的状态很少

不胡的状态有\(2091\)个,可以直接开一个\(map\)来记录,这里要记得重载小于号

将期望看成,如果摸了\(i(i≥13)\)张牌,如果仍然不胡则贡献\(1\)

答案需要求出有\(i\)张牌时仍然不胡的方案数,并乘一个排列数\((4n-i)!\)

仍然考虑dp,设\(f_{i,j,k}\)表示考虑\(1...i\),当前状态为\(j\),一共选了\(k\)张牌的方案数

转移时枚举\(i+1\)面值选多少张即可

总复杂度\(O(n^2S)\)\(S\)表示状态数


Code 

#include<bits/stdc++.h>
using namespace std;
#define reg register
inline int read()
{
    int 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,C[5][5]={{1},{1,1},{1,2,1},{1,3,3,1},{1,4,6,4,1}};
int Mul(int x,int y){return (1ll*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
int n,w[15],t[105];
void rw(int &x,int y){if(min(y,4)>x)x=y;}
struct State
{
    int f[18],cnt;
    State(){memset(f,-1,sizeof f);f[0]=cnt=0;}
    bool operator <(const State&o)const
    {
        if(cnt!=o.cnt) return (cnt<o.cnt);
        for(int i=0;i<18;++i)
            if(f[i]!=o.f[i]) return (f[i]<o.f[i]);
        return false;
    }
    bool HU(){return cnt>=7||f[9]>=4;}
    friend State Trans(State a,int b)
    {
        register int i,j,k;State c;c.cnt=a.cnt+(b>=2);
        for(i=0;i<3;++i)for(j=0;j<3;++j)for(k=0;k<3&&i+j+k<=b;++k)
        {
            if(~a.f[i*3+j]) rw(c.f[j*3+k],a.f[i*3+j]+i+(b-i-j-k)/3);
            if(~a.f[i*3+j+9])rw(c.f[9+j*3+k],a.f[9+i*3+j]+i+(b-i-j-k)/3);
            if(i+j+k+2<=b&&~a.f[i*3+j]) rw(c.f[9+j*3+k],a.f[i*3+j]+i);
        }
        return c;
    }
}st[2100];int tot;int tr[2100][5];
std::map<State,int> mp;
int dfs(State cur)
{
    if(cur.HU()) return 0;
    if(mp.find(cur)!=mp.end()) return mp[cur];
    st[mp[cur]=++tot]=cur;int now=tot;
    for(int i=0;i<=4;++i) tr[now][i]=dfs(Trans(cur,i));
    return now;
}
int f[2][2100][405],fac[450],inv[450],ans;
int main()
{
    dfs(State());
    register int sum,i,j,k,l;
    for(inv[0]=inv[1]=fac[0]=fac[1]=1,i=2;i<=420;++i)
        fac[i]=Mul(fac[i-1],i),inv[i]=Mul((P-P/i),inv[P%i]);
    for(i=2;i<=420;++i) inv[i]=Mul(inv[i],inv[i-1]);
    n=read();
    for(i=1;i<=13;++i) w[i]=read(),read(),++t[w[i]];
    f[0][1][0]=1;

    for(sum=0,i=1;i<=n;sum+=t[i],++i)
    {
        memset(f[i&1],0,sizeof f[i&1]);
        for(j=1;j<=tot;++j)for(k=sum;k<=(i-1)<<2;++k)if(f[(i&1)^1][j][k])for(l=t[i];l<=4;++l)if(tr[j][l])
        {
            int nxt=tr[j][l];
            f[i&1][nxt][k+l]=Add(f[i&1][nxt][k+l],
                Mul(f[(i&1)^1][j][k],Mul(C[4-t[i]][l-t[i]],Mul(inv[k-sum],fac[k+l-sum-t[i]]))));
        }
    }

    ans=0;
    for(i=0;i<=(n<<2);ans=Add(ans,Mul(l,fac[(n<<2)-i])),++i)
        for(l=0,j=1;j<=tot;l=Add(l,f[n&1][j][i]),++j);

    printf("%d\n",Mul(ans,inv[(n<<2)-13]));
    return 0;
}


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

posted @ 2019-07-22 20:46  PaperCloud  阅读(414)  评论(0编辑  收藏  举报