zoukankan      html  css  js  c++  java
  • 洛谷P5279 [ZJOI2019]麻将(乱搞+概率期望)

    题面

    传送门

    题解

    看着题解里一堆巨巨熟练地用着专业用语本萌新表示啥都看不懂啊……顺便(orz)余奶奶

    我们先考虑给你一堆牌,如何判断能否胡牌

    我们按花色大小排序,设(dp_{0/1,i,j,k})表示是否有对子,考虑了前(i)种花色的牌,选了(j)个以(i-1)为开头的顺子(三个连续牌),(k)个以(i)为开头的顺子,此时能选的最大面子数。转移的话枚举以(i+1)为开头的顺子的个数,剩下的组成刻子(三个相同牌)就好了(加一个数字记为(Trans)

    那么胡牌的条件有两个,(exists i,j,dp_{1,n,j,k}geq 4),或者记(cnt)为牌数(geq 2)的花色数,(cntgeq 7)

    这个东西和(i)这一维关系不是很大,我们可以把它当成一个(3 imes 3)的矩阵来转移。我们可以用一个结构体(node)来表示这个矩阵,并且为了维护(dp_0)(dp_1)我们需要开两个矩阵,并且记录(cnt),把这些所有都放到一个(Mahjong)结构体里。据说可行的(Mahjong)的状态只有(3956)

    然后期望可以转化为(ans=sumlimits_{i=13}^{4n}p(i)),其中(p(i))表示摸了(i)张牌还不能胡的概率,所以转化为算摸了(i)张牌还不能胡的排列数

    (f_{i,j,k})表示我们考虑了前(i)种花色的牌,当前(Mahjong)的状态为(j),已经摸了(k)张牌的排列数是多少

    转移的时候,我们枚举摸了花色(i+1)的张数(z),那么就可以转移到(f_{i+1,Trans(j,z),k+z}),乘上的系数是((4 - org_{i + 1})^{underline{z - org{i + 1}}} inom{k + z - sum_{i + 1}}{z - org_{i + 1}})(org_i) 表示原有的 (13) 张牌中花色为 (i) 的有几张, (sum) 则是 (org) 的前缀和,这个式子的意思就是我们需要在没被选过的 (4 - org_{i + 1}) 张牌中选 (z - org_{i + 1}) 张的排列,并且插入到之前的排列中,但前 (13) 张牌的顺序是固定的。

    最后(p_i)就等于摸了(i)张牌不赢的排列数减去摸(i)张牌的排列数

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    using namespace std;
    const int N=105,M=4005,P=998244353;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
        R int res=1;
        for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
        return res;
    }
    int C[M][5],fac[5];
    void init(){
        fp(i,0,M-1){
            C[i][0]=1;
            fp(j,1,min(4,i))C[i][j]=add(C[i-1][j],C[i-1][j-1]);
        }
        fac[0]=1,fac[1]=1,fac[2]=2,fac[3]=6,fac[4]=24;
    }
    struct node{
        int a[3][3];
        node(){memset(a,-1,sizeof(a));}
        inline void init(){memset(a,-1,sizeof(a));}
        inline int* operator [](const int &x){return a[x];}
        bool operator <(node b)const{
            fp(i,0,2)fp(j,0,2)if(a[i][j]!=b[i][j])return a[i][j]<b[i][j];
            return 0;
        }
        bool operator ==(node b)const{
            fp(i,0,2)fp(j,0,2)if(a[i][j]!=b[i][j])return false;
            return true;
        }
        friend node Max(node a,node b){
            fp(i,0,2)fp(j,0,2)cmax(a[i][j],b[i][j]);
            return a;
        }
        friend node Trans(node a,int b){
            node res;
            fp(i,0,2)fp(j,0,2)if(~a[i][j])
                fp(k,0,min(2,b-i-j))
                    cmax(res[j][k],min(a[i][j]+i+(b-i-j-k)/3,4));
            return res;
        }
    };
    struct Mahjong{
        node p[2];int cnt;
        Mahjong(){p[0].init(),p[1].init(),p[0][0][0]=cnt=0;}
        inline bool operator <(const Mahjong &b)const{
            return cnt==b.cnt?p[0]==b.p[0]?p[1]<b.p[1]:p[0]<b.p[0]:cnt<b.cnt;
        }
        friend Mahjong Trans(Mahjong a,int b){
            a.cnt=min(a.cnt+(b>=2),7);
            a.p[1]=Trans(a.p[1],b);
            if(b>=2)a.p[1]=Max(a.p[1],Trans(a.p[0],b-2));
            a.p[0]=Trans(a.p[0],b);
            return a;
        }
        bool right(){
            if(cnt==7)return true;
            fp(i,0,2)fp(j,0,2)if(p[1][i][j]==4)return true;
            return false;
        }
    }mahjong[M];map<Mahjong,int>mp;
    bool win[M];int n,tot,s[N],f[N][M][N<<2],trans[M][5];
    void dfs(Mahjong now){
        if(mp.count(now))return;
        mahjong[++tot]=now,mp[now]=tot,win[tot]=now.right();
        fp(i,0,4)dfs(Trans(now,i));
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
        init(),dfs(Mahjong());
        fp(i,1,tot)fp(j,0,4)trans[i][j]=mp[Trans(mahjong[i],j)];
        scanf("%d",&n);
        for(R int i=1,x;i<=13;++i)scanf("%d%*d",&x),++s[x];
        f[0][1][0]=1;
        for(R int i=0,sum=0;i<n;++i){
            sum+=s[i+1];
            fp(j,1,tot)fp(l,s[i+1],4){
                int *nf=f[i+1][trans[j][l]],*now=f[i][j];
                int tmp=mul(C[4-s[i+1]][l-s[i+1]],fac[l-s[i+1]]);
                fp(k,0,(n<<2)-l)if(now[k])
                    upd(nf[k+l],mul(now[k],mul(C[k+l-sum][l-s[i+1]],tmp)));
            }
        }
        int ans=0;
        for(R int i=13,res=1,up;i<=(n<<2);++i){
            up=0;
            fp(j,1,tot)if(!win[j])upd(up,f[n][j][i]);
            upd(ans,mul(up,ksm(res,P-2))),res=mul(res,(n<<2)-i);
        }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    DataGrid内容导出Excel文件(C#)
    IE无法查看源文件原因及解决办法
    通过Internet访问内网中的服务器
    工欲善其事,必先利其器——图文并茂详解VisualStudio使用技巧
    Flash中对动态文本框使用遮罩
    Flash中XML跨域访问数据的规则
    Google导航代码
    信息竞赛小结
    第一天,开始系统学习 void
    浅析各种数据类型的取值范围 void
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10679698.html
Copyright © 2011-2022 走看看