zoukankan      html  css  js  c++  java
  • 「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
    ",Mul(ans,inv[(n<<2)-13]));
        return 0;
    }
    


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

  • 相关阅读:
    7个简单但棘手的JavaScript面试问题
    Promise.then链式调用顺序
    JS模拟实现枚举
    web前端常见的加密算法介绍
    JavaScript浮点数精度
    工作中常用的JavaScript函数片段
    JavaScript Array (数组) 对象
    快速找出数组中是否包含公共项
    用JavaScript让你的浏览器说话
    JavaScript的声明提升
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/11228285.html
Copyright © 2011-2022 走看看