• [ZJOI2019]麻将


    https://www.luogu.org/problemnew/show/P5279

    题解

    我们可以把一副牌看成一个字符串,字符串的第(i)位表示编号为(i)的牌有多少张。

    注意一下胡牌的条件:1、四面+一对2、七对。

    我们可以设计一个(dp)

    (dp[i][j][k][0/1])表示已经做完了前(i)种权值,保留了((i-1,i))(j)对,除此之外还有(k)(i),除此之外(0)不保留(1)保留了对子时,面子数的最大值。

    转移的话枚举下一种牌型放几张牌,然后枚举转移之后的(k),那么剩下的(x-k)张牌需要全部和前面的(i)个对子组成面子,和前面的(j)个单的组成对,所以我们的(i+j+k)不能超过新来点的牌的总和。

    还有我们注意到当(j)或者(k)超过(2)时是可以直接组成面子的,所以我们的一个(dp)数组时(3 imes 3)的。

    对于(0)(1)的转移,注意到当新来的牌超过(1)张时是可以从(0)转移到(1)的。

    注意到我们刚才的(dp)状态中没有提到第二种胡牌的方式,这个我们可以在(dp)值中加上一个对子的个数来判断。

    这个(dp)的第一维可以滚掉,于是我们的(dp)状态就变成了,当前牌有多少对牌,还有两个转移矩阵。

    我们可以把这个(dp)的转移看做一个自动机,然后用(bfs)的方法去扩展,对于胡了的牌我们开一个节点,所有胡牌的转移边全部连上去。然后我们发现这个状态量只有(2092)

    于是我们可以设(f[i][j][k])表示做到前(i)种牌,从后面的牌中拿了(j)张,现在在自动机的(k)节点上。

    (dp)完之后我们相当于求出了取出(i)张牌每胡的概率,那么期望次数就是。

    [(sum dp[i])+1 ]

    代码

    #include<bits/stdc++.h>
    #define M 2100
    #define N 402
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    ll dp[2][N][M],jie[N],ni[N];
    int sum,nowsum,tot,a[N],n;
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    inline ll power(ll x,ll y){
      ll ans=1;
      while(y){if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;}
      return ans;
    }
    inline ll C(int n,int m){return jie[n]*ni[m]%mod*ni[n-m]%mod;}
    inline ll calc(int now,int x,int y){
      return dp[now][x][y]*jie[x]%mod*jie[nowsum-x]%mod;
    }
    inline void MOD(ll &x){x=x>=mod?x-mod:x;}
    struct matrix{
      int a[3][3];
      matrix(){memset(a,-1,sizeof(a));}
      inline bool operator !=(const matrix &b)const{
        for(int i=0;i<3;++i)
          for(int j=0;j<3;++j)
            if(a[i][j]!=b.a[i][j])return 1;
        return 0;
      }
      inline bool operator <(const matrix &b)const{
        for(int i=0;i<3;++i)
          for(int j=0;j<3;++j)
            if(a[i][j]!=b.a[i][j])return a[i][j]<b.a[i][j];
        return 0;
      }
      inline bool hu(){
        for(int i=0;i<3;++i)
          for(int j=0;j<3;++j)if(a[i][j]>=4)return 1;
        return 0;
      }
      inline void add(matrix b,int num){
        for(int i=0;i<3;++i)
          for(int j=0;j<3;++j)if(~b.a[i][j])
             for(int k=0;k<3&&i+j+k<=num;++k){
               a[j][k]=max(a[j][k],min(4,b.a[i][j]+i+(num-i-j-k)/3));
             }
      }
    };
    struct node{
      matrix p[2];
      int cnt,ch[5];
      node(){cnt=0;memset(ch,0,sizeof(ch));p[0]=p[1]=matrix();}
      inline bool hu(){
        if(cnt>=7)return 1;return p[1].hu();
      }
      inline bool operator <(const node &b)const{
        if(cnt!=b.cnt)return cnt<b.cnt;
        if(p[0]!=b.p[0])return p[0]<b.p[0];
        return p[1]<b.p[1];
      }
      inline void boom(){
        memset(ch,0,sizeof(ch));
        p[0]=p[1]=matrix();cnt=-1;
      }
      friend inline node operator +(node a,int num){
        if(a.hu()){a.boom();return a;}
        node t;
        t.p[0].add(a.p[0],num);
        t.p[1].add(a.p[1],num);
        t.cnt=a.cnt+(num>=2);
        if(num>=2)t.p[1].add(a.p[0],(num-2));
        if(t.hu())t.boom();
        return t;
      }
    };
    node nw[2100];
    map<node,int>mp;
    inline void bfs(int id){
      for(int i=0;i<5;++i){
        node x=nw[id]+i;
        if(mp.find(x)==mp.end())nw[mp[x]=++tot]=x;
        nw[id].ch[i]=mp[x];
      }
    }
    inline void prework(int n){
      jie[0]=1;
      for(int i=1;i<=n;++i)jie[i]=jie[i-1]*i%mod;
      ni[n]=power(jie[n],mod-2);
      for(int i=n-1;i>=0;--i)ni[i]=ni[i+1]*(i+1)%mod;
      tot=2;
      nw[1].p[0].a[0][0]=0;
      mp[nw[1]]=1;
      nw[tot].cnt=-1;
      mp[nw[tot]]=2;
      bfs(1);
      for(int i=3;i<=tot;++i)bfs(i);
        
    }
    int main(){
        n=rd();
        sum=n*4;
        nowsum=sum-13;
        prework(sum);
        for(int i=1;i<=13;++i){
          int x=rd(),y=rd();
          a[x]++;
        }
        int now=1,pre=0;
        dp[now][0][1]=1;
        for(int i=1;i<=n;++i){
          swap(now,pre);
          memset(dp[now],0,sizeof(dp[now]));
          for(int j=0;j<=nowsum;++j){
            for(int k=1;k<=tot;++k)if(dp[pre][j][k])
              for(int l=0;l<=4-a[i];++l){
                MOD(dp[now][j+l][nw[k].ch[l+a[i]]]+=dp[pre][j][k]*C(4-a[i],l)%mod);
              }
          }
        }
        ll ans=0;
        for(int i=1;i<=tot;++i)if(i!=2){
          for(int j=1;j<=nowsum;++j)MOD(ans+=calc(now,j,i));
        }
        ans=ans*ni[nowsum]%mod;
        MOD(ans+=1);
        cout<<ans;
        return 0;
    }
    
  • 相关阅读:
    HDU 1335 Basically Speaking(进制转换)
    2016年团体程序设计天梯赛-决赛 L2-3. 互评成绩(25)
    2016年团体程序设计天梯赛-决赛 L1-8. Left-pad(20)
    2016年团体程序设计天梯赛-决赛 L1-7. 到底是不是太胖了(10)
    2016年团体程序设计天梯赛-决赛 L1-6. 一帮一(15)
    2016年团体程序设计天梯赛-决赛 L1-5. 是不是太胖了(5)
    2016年团体程序设计天梯赛-决赛 L1-3. 出租(20)
    2016年团体程序设计天梯赛-决赛 L1-2. I Love GPLT(5)
    2016年团体程序设计天梯赛-决赛 L1-1. 正整数A+B(15)
    Codeforces Round #321 (Div. 2) C. Kefa and Park dfs
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/11050328.html
Copyright © 2020-2023  润新知