zoukankan      html  css  js  c++  java
  • 洛谷P5279 [ZJOI2019]麻将

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

    以下为个人笔记,建议别看:

    首先考虑如何判一个牌型是否含有胡的子集。先将牌型表示为一个数组num,其中num[i]表示牌i出现了几张。

    先判七对子(略)。

    然后做一个dp。(后面的算法不支持"在最后(i接近n时)进行特判的dp",如果"在开始(i为1,2,3时)进行特判"也可能难以实现,因此可能需要改进一下dp。)

    ans[i][j][k][l]表示考虑前i种花色的牌,是否预留了对子(j为1有,j为0无),顺子(i-1,i,i+1)取k个,顺子(i,i+1,i+2)取l个,把剩余的第1~i种的牌都尽量组成刻子,最多能得到多少个面子(这些顺子自身的贡献要等取到最大的那个数时再算,可以避免一些边界处理,比如不会出现(1,0,-1),(n+1,n,n-1)之类的顺子)。由于3个相同的顺子等同于3个刻子,只需要考虑0<=k<=2,0<=l<=2即可。当且仅当ans[n][1][0..2][0..2]的最大值>=4时牌可以胡。(转移略)

    可以得到这样一个暴力

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 using namespace std;
      5 #define fi first
      6 #define se second
      7 #define pb push_back
      8 typedef long long ll;
      9 typedef unsigned long long ull;
     10 
     11 inline void setmax(int &a,int b) 
     12 {
     13     if(a<b)    a=b;
     14 }
     15 
     16 int num[111],n;
     17 inline char judge()
     18 {
     19     int t1=0,i,k,l,tt,ed;
     20     for(i=1;i<=n;++i)
     21         if(num[i]>=2)
     22             ++t1;
     23     if(t1>=7)    return 1;
     24     static int ok[115][2][3][3];
     25     memset(ok,192,sizeof(ok));//(i-1,i,i+1)->k,(i,i+1,i+2)->l
     26     ok[0][0][0][0]=0;
     27     for(i=0;i<n;++i)
     28     {
     29         for(k=0;k<=2;++k)
     30         {
     31             for(l=0;l<=2;++l)
     32             {
     33                 ed=min(2,num[i+1]-k-l);
     34                 for(tt=0;tt<=ed;++tt)
     35                 {
     36                     setmax(ok[i+1][0][l][tt],ok[i][0][k][l]+k+(num[i+1]-k-l-tt)/3);
     37                     setmax(ok[i+1][1][l][tt],ok[i][1][k][l]+k+(num[i+1]-k-l-tt)/3);
     38                 }
     39                 ed=min(2,num[i+1]-k-l-2);
     40                 for(tt=0;tt<=ed;++tt)
     41                 {
     42                     setmax(ok[i+1][1][l][tt],ok[i][0][k][l]+k+(num[i+1]-k-l-tt-2)/3);
     43                 }
     44             }
     45         }
     46     }
     47     int ans=0;
     48     for(k=0;k<=2;++k)
     49         for(l=0;l<=2;++l)
     50             setmax(ans,ok[n][1][k][l]);
     51     return ans>=4;
     52 }
     53 const int md=998244353;
     54 #define addto(a,b) ((a)+=(b),((a)>=md)&&((a)-=md))
     55 int fac[511],ifac[511];
     56 int ans;
     57 /*
     58 ull ttttt[555];
     59 void out()
     60 {
     61             for(int i=1;i<=200;++i)
     62                 printf("%llu ",ttttt[i]);
     63             puts("");
     64             int t;
     65             scanf("%d",&t);
     66 }
     67 */
     68 void dfs(int p,int ddd)
     69 {
     70     if(judge())
     71     {
     72         addto(ans,ull(p)*ddd%md*fac[4*n-13-p]%md);
     73         /*
     74         ttttt[p]+=ddd;++ttttt[0];
     75         if(ttttt[0]%10000==0)
     76         {
     77             out();
     78         }
     79         */
     80         //if(p<10)
     81         //printf("%d
    ",p);
     82         return;
     83     } 
     84     for(int i=1;i<=n;++i)
     85         if(num[i]<4)
     86         {
     87             ++num[i];
     88             dfs(p+1,ull(ddd)*(5-num[i])%md);
     89             --num[i];
     90         }
     91 }
     92 int poww(int a,int b)
     93 {
     94     int ans=1;
     95     for(;b;b>>=1,a=ull(a)*a%md)
     96         if(b&1)
     97             ans=ull(ans)*a%md;
     98     return ans;
     99 }
    100 int main()
    101 {
    102     /*
    103     int i,w,t;
    104     scanf("%d%d",&n,&t);
    105     for(i=1;i<=t;++i)
    106     {
    107         scanf("%d",&w);
    108         ++num[w];
    109     }
    110     printf("%d
    ",int(judge()));
    111     */
    112     int i,w,t;
    113     fac[0]=1;
    114     for(i=1;i<=500;++i)
    115         fac[i]=ull(fac[i-1])*i%md;
    116     ifac[500]=poww(fac[500],md-2);
    117     for(i=500;i>=1;--i)
    118         ifac[i-1]=ull(ifac[i])*i%md;
    119     scanf("%d",&n);
    120     for(i=1;i<=13;++i)
    121     {
    122         scanf("%d%d",&w,&t);
    123         ++num[w];
    124     }
    125     dfs(0,1);
    126     printf("%llu
    ",ull(ans)*ifac[4*n-13]%md);
    127     //out();
    128     return 0;
    129 }
    View Code

    可以根据这个dp建成一个类似自动机的东西。自动机上的状态(点)可以当做一个三维数组再加上一个数字,三维就是就是ans的后三维,数组的元素就是在那三维的条件下最多凑出的面子数,加上的数字是"有多少种数字的牌可以凑出对子"(为了把七对子的统计放进自动机)。转移边就是根据dp的转移来连。对于起始状态,显然额外数字为0,设数组为a,则数组中只有a[0][0][0]=0,其余全为-inf。可以用和开头一样的方法判断一个状态是否是胡牌状态。为了方便,可以把所有胡牌的状态合并成一个状态,它的所有转移边都指向自身。

    爆搜一下,可以发现这个自动机的状态并不是很多(不知道为什么)。爆搜的方法就是搞一个bfs,队列中一开始只有初始状态,每次从队列首部取出一个状态,枚举下一个数牌数量是0/1/2/3/4进行转移,得到它的后继状态。如果后继状态胡了:直接向某一个钦点的结束状态连边连边。如果后继状态没有胡:如果没有遍历过后继状态就建立后继状态对应的点并连边,然后将后继状态加入队列,否则直接向后继状态连边。判断后继状态是否遍历过可以强行搞一个map之类的。为了复杂度对,需要让数组中各个值对4取min,额外数字对7取min(这一步的确是有必要的,因为可能a[0]里面有很大的数字,但是a[1]里面都很小,虽然有很大数字,仍然不能胡牌,导致有无限个状态)

    如何根据这个自动机计算答案?(以下“能胡”指存在一个子集胡牌)

    最终的答案=所有方案胡牌巡数的平均值=所有方案胡牌巡数总和/方案数

    此处的一种方案:给剩余的未摸进来的牌每张一个(a,b)的编号,表示数字为a的第b张牌;对于这些(a,b)对的任意一个排列就是一种方案。

    先算所有方案胡牌巡数总和。把每个方案拆开,拆成没胡牌前每一巡1的贡献,胡牌那一巡1的贡献,两者分开考虑。对于两个部分,都将所有方案一起考虑。对于第一部分,每一巡分开考虑,相当于每一巡的贡献是这一巡有多少方案不能胡。对于第二部分,由于所有牌摸进来必定能胡,贡献就是方案数。

    胡牌巡数总和除以方案数,得到答案=$frac{sum_{i=1}^{4n-13}额外摸i张牌不能胡的方案数}{总方案数}+1$

    怎么算这个东西?首先,总方案数等于$(4n-13)!$

    dp一下,ans[i][j][k]表示考虑前i种牌,额外摸了j张,当前在自动机上状态是k的方案数(考虑最终答案,额外摸i张牌不能胡的方案,相当于先从所有(a,b)对中选出i个,让它们作为前i张摸上来的牌,如果它们不能胡,则产生贡献i!(4n-13-i)!;因此此处的一种方案定义为从前i种牌产生的所有(a,b)对中选出j个,最后统计答案时ans[n][j][k]只有当k!=T时才产生贡献,对答案的贡献要乘上j!(4n-13-j)!)(转移略)

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<vector>
      5 #include<map>
      6 using namespace std;
      7 #define fi first
      8 #define se second
      9 #define mp make_pair
     10 #define pb push_back
     11 typedef long long ll;
     12 typedef unsigned long long ull;
     13 const int md=998244353;
     14 #define addto(a,b) ((a)+=(b),((a)>=md)&&((a)-=md))
     15 inline void setmax(int &a,int b)
     16 {
     17     if(a<b && b>=0)    a=b;
     18 }
     19 inline void setmin(int &a,int b)
     20 {
     21     if(a>b)    a=b;
     22 }
     23 struct st
     24 {
     25     int a[2][3][3],b;
     26 };
     27 inline bool operator<(const st &a,const st &b)
     28 {
     29     /*
     30     int t=memcmp(a.a,b.a,sizeof(a.a));
     31     if(!t)    return a.b<b.b;
     32     else    return t<0;
     33     */
     34     for(int i=0;i<=1;++i)
     35         for(int j=0;j<=2;++j)
     36             for(int k=0;k<=2;++k)
     37                 if(a.a[i][j][k]!=b.a[i][j][k])
     38                     return a.a[i][j][k]<b.a[i][j][k];
     39     return a.b<b.b;
     40 }
     41     inline bool judge(const st &a)
     42     {
     43         if(a.b>=7)    return 1;
     44         int j,k;
     45         for(j=0;j<3;++j)
     46             for(k=0;k<3;++k)
     47                 if(a.a[1][j][k]>=4)
     48                     return 1;
     49         return 0;
     50     }
     51     inline void nxt_state(const st &a,st &b,int x)
     52     {
     53         b.b=min(7,a.b+(x>=2));
     54         memset(b.a,192,sizeof(b.a));
     55         int i,j,k;
     56         for(i=0;i<=2;++i)
     57             for(j=0;j<=2;++j)
     58             {
     59                 for(k=0;k<=min(2,x-i-j);++k)
     60                 {
     61                     setmax(b.a[0][j][k],a.a[0][i][j]+i+(x-i-j-k)/3);
     62                     setmax(b.a[1][j][k],a.a[1][i][j]+i+(x-i-j-k)/3);
     63                 }
     64                 for(k=0;k<=min(2,x-i-j-2);++k)
     65                 {
     66                     setmax(b.a[1][j][k],a.a[0][i][j]+i+(x-i-j-k-2)/3);
     67             }
     68         }
     69         for(i=0;i<=2;++i)
     70             for(j=0;j<=2;++j)
     71             {
     72                 setmin(b.a[0][j][k],4);
     73                 setmin(b.a[1][j][k],4);
     74             }
     75 
     76 }
     77 map<st,int> ma;
     78 int trans[4011][5];
     79 /*
     80 struct E
     81 {
     82     int to,nxt;
     83 }e[200011];
     84 int f1[10011],ne;
     85 inline void me(int x,int y)
     86 {
     87     e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;
     88 }
     89 */
     90 int mem,S,T;st ta[4011];
     91 /*
     92 void out()
     93 {
     94     printf("%d %d
    ",S,T);
     95     for(int i=1;i<=mem;++i)
     96     {
     97         printf("id%d
    ",i);
     98         for(int j=0;j<=1;++j)
     99         {
    100             for(int k=0;k<=2;++k)
    101             {
    102                 for(int l=0;l<=2;++l)
    103                 {
    104                     printf("%d ",ta[i].a[j][k][l]);
    105                 }
    106                 puts("");
    107             }
    108             puts("/////////////////////");
    109         }
    110         for(int j=0;j<=4;++j)
    111             printf("%d ",trans[i][j]);
    112         puts("");
    113         printf("%d
    ---------------------
    ",ta[i].b);
    114     }
    115 }
    116 */
    117 void init()
    118 {
    119     st t1,t2;int t,i;
    120     T=++mem;
    121     S=++mem;
    122     memset(t1.a,192,sizeof(t1.a));
    123     t1.b=0;
    124     t1.a[0][0][0]=0;
    125     ma[t1]=S;ta[S]=t1;
    126     for(t=S;t<=mem;++t)
    127     {
    128         t1=ta[t];
    129         for(i=0;i<=4;++i)
    130         {
    131             nxt_state(t1,t2,i);
    132             if(judge(t2))
    133                 trans[t][i]=T;
    134             else if(!ma.count(t2))
    135             {
    136                 ma[t2]=++mem;
    137                 ta[mem]=t2;
    138                 trans[t][i]=mem;
    139             }
    140             else
    141                 trans[t][i]=ma[t2];
    142         }
    143     }
    144     for(i=0;i<=4;++i)
    145         trans[T][i]=T;
    146 }
    147 
    148 int n1[101],n,ans;
    149 int an1[101][389][2101];
    150 int fac[10011],ifac[10011];
    151 int C(int n,int m)    {return ull(fac[n])*ifac[m]%md*ifac[n-m]%md;}
    152 int CC[6][6];
    153 int main()
    154 {
    155     int i,t1,t2,j,k,l;
    156     fac[0]=1;
    157     for(i=1;i<=10000;++i)
    158         fac[i]=ull(fac[i-1])*i%md;
    159     //printf("1t%d
    ",fac[10000]);
    160     ifac[10000]=265002293;
    161     for(i=10000;i>=1;--i)
    162         ifac[i-1]=ull(ifac[i])*i%md;
    163     //printf("2t%d
    ",ifac[1]);
    164     init();
    165     for(i=0;i<=5;++i)
    166         for(j=0;j<=i;++j)
    167             CC[i][j]=C(i,j);
    168     /*
    169     printf("1t%d
    ",mem);
    170     for(i=21;i<=25;++i)
    171     {
    172         for(int j=0;j<=4;++j)
    173             printf("%d ",trans[i][j]);
    174         puts("");
    175         for(int j=0;j<=2;++j)
    176         {
    177             for(int k=0;k<=2;++k)
    178                 printf("%d ",ta[i].a[0][j][k]);
    179             puts("");
    180         }
    181         puts("");
    182         for(int j=0;j<=2;++j)
    183         {
    184             for(int k=0;k<=2;++k)
    185                 printf("%d ",ta[i].a[1][j][k]);
    186             puts("");
    187         }
    188         puts("");
    189     }
    190     return 0;
    191     */
    192     //printf("1t%d
    ",mem);
    193     scanf("%d",&n);
    194     for(i=1;i<=13;++i)
    195     {
    196         scanf("%d%d",&t1,&t2);
    197         ++n1[t1];
    198     }
    199     an1[0][0][S]=1;
    200     for(i=0;i<n;++i)
    201     {
    202         for(j=0;j<=4*n-13;++j)
    203         {
    204             for(k=1;k<=mem;++k)
    205             {
    206                 for(l=0;l<=4-n1[i+1];++l)
    207                 {
    208                     addto(an1[i+1][j+l][trans[k][l+n1[i+1]]],ull(an1[i][j][k])*CC[4-n1[i+1]][l]%md);
    209                     //预处理C(a,b)减小常数
    210                 }
    211             }
    212         }
    213     }
    214     for(j=1;j<=4*n-13;++j)
    215     {
    216         for(k=1;k<=mem;++k)
    217             if(k!=T)
    218             {
    219                 addto(ans,ull(an1[n][j][k])*fac[j]%md*fac[4*n-13-j]%md);
    220             }
    221     }
    222     printf("%llu
    ",(ull(ans)*ifac[4*n-13]+1)%md);
    223     return 0;
    224 }
    View Code
  • 相关阅读:
    B.Icebound and Sequence
    Educational Codeforces Round 65 (Rated for Div. 2) D. Bicolored RBS
    Educational Codeforces Round 65 (Rated for Div. 2) C. News Distribution
    Educational Codeforces Round 65 (Rated for Div. 2) B. Lost Numbers
    Educational Codeforces Round 65 (Rated for Div. 2) A. Telephone Number
    Codeforces Round #561 (Div. 2) C. A Tale of Two Lands
    Codeforces Round #561 (Div. 2) B. All the Vowels Please
    Codeforces Round #561 (Div. 2) A. Silent Classroom
    HDU-2119-Matrix(最大匹配)
    读书的感想!
  • 原文地址:https://www.cnblogs.com/hehe54321/p/10728617.html
Copyright © 2011-2022 走看看