• 洛谷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
  • 相关阅读:
    CDN缓存服务器现状,squid、nginx、trafficserver、ATS性能测试
    [钉钉通知系列]Jenkins发布后自动通知
    查税号,税号查询,纳税人识别号查询
    体验万象优图鉴黄服务
    红象云腾
    我眼中的领域驱动设计
    手工DIY:手机变身扫描仪_极客迷
    【CZURET-16】成者科技(CZUR)ET16智能扫描仪OCR文字识别书籍文档票据零边距高速A3A4高拍仪高清1600万像素【行情 报价 价格 评测】-京东
    技术漫谈 | 使用docker-compose进行python开发
    饿了么的 PWA 升级实践
  • 原文地址:https://www.cnblogs.com/hehe54321/p/10728617.html
Copyright © 2020-2023  润新知