这题好牛逼啊。。。

虽然说也是 DP 套 DP,但是感觉比 TJOI 那题高明到哪里去了(

我们先考虑如何计算期望。如果设 \(f_i\) 为拿到 \(i\) 张牌后胡的方案数,这个并不是很好做,因为你要考虑前面不胡。

所以我们设 \(f_i\) 为手上有 \(13+i\) 张牌但还没有胡的方案数,那么答案就是:

\[\frac{\sum_{i=1}^{4n-13}f_ii!(4n-13-i)!}{(4n-14)!}+1
\]

发现一个手上牌的顺序不影响是否胡,所以有两个阶乘用来打乱排序(因为牌与牌直接互不相同)。

我们考虑外层的 DP 建立一个“胡牌自动机”,所以需要写一个用来判定是否胡牌的 DP。

加入新的牌就是在自动机上面走一步。我们设 \(dp_{i,x,y,0/1}\) 为加入了前 \(i\) 类牌,现在手上有 \(x\) 对 \((i-1,i)\) 的牌和 \(y\) 张 \(i\),\(0/1\) 表示当前无/有对子。表示的是在这个状态下面子最多个数。

我们将发现如果 \(x,y\geq 3\),那么可以自动对出来一个面子,所以有 \(0\leq x,y<3\)。

我们把这两个维度写到矩阵上面。然后交换维度变成 \(dp_{i,0/1,x,y}\)。

考虑加入 \(x\) 张牌时如何从 \(dp_{i-1}\) 转移到 \(dp_i\)。

考虑加入 \(x\) 张牌的时候,先凑到 \((i-2,i-1)\) 里面去,然后自己和自己凑面子,最后加上已经有的面子个数。

如果 \(x\geq 2\),那么我们可以砍掉一对对子再加进去。

也就是:\((f_{i-1,0},x)\to f_{i,0},(f_{i-1,1},x)\to f_{i,1},(f_{i-1,0},x-2)\to f_{i,1}\)。

然后我们再额外记录对子个数即可。

然后我们用一个 std::map 离散化一下就可以建出“胡牌自动机”了。

这个是主要的,在这个 DFA 上面 DP 反而很简单。

直接考虑当前加 \(x\) 张牌,然后从牌堆里面把这 \(x\) 张牌选出来,丢到下一个 DFA 节点里面去就可以了。

我们可以把所有胡掉的节点压成一个节点,然后标记 \(t=-1\),可以节省空间。

  1. #include<cstdio>
  2. #include<map>
  3. typedef int ui;
  4. const ui M=105,mod=998244353;
  5. const ui C[5][5]={{1,0,0,0,0},{1,1,0,0,0},{1,2,1,0,0},{1,3,3,1,0},{1,4,6,4,1}};
  6. ui n,m,a[M],fac[M*4],ifac[M*4],dp[2][M*4][2100];
  7. inline ui min(const ui&a,const ui&b){
  8. return a>b?b:a;
  9. }
  10. inline void max(ui&a,const ui&b){
  11. if(b>a)a=b;
  12. }
  13. struct HuAutomation{
  14. ui tot;
  15. struct Matrix{
  16. #define For for(ui i=0;i^3;++i)for(ui j=0;j^3;++j)
  17. ui p[3][3];
  18. Matrix(){
  19. For p[i][j]=-1;
  20. }
  21. inline ui*operator[](const ui&x){
  22. return p[x];
  23. }
  24. inline bool operator==(Matrix q)const{
  25. For if(p[i][j]!=q[i][j])return false;return true;
  26. }
  27. inline bool operator<(Matrix q)const{
  28. For if(p[i][j]!=q[i][j])return p[i][j]<q[i][j];return false;
  29. }
  30. inline bool CheckHu()const{
  31. For if(~p[i][j]&&p[i][j]>3)return true;return false;
  32. }
  33. inline void Update(Matrix bel,const ui&x){
  34. For if(~bel[i][j])for(ui k=0;k^3&&i+j+k<=x;++k){
  35. max(p[j][k],min(i+bel[i][j]+(x-i-j-k)/3,4));
  36. }
  37. }
  38. #undef For
  39. };
  40. struct Node{
  41. ui t;Matrix P[2];
  42. inline Node(){
  43. t=0;P[0]=P[1]=Matrix();
  44. }
  45. inline bool operator<(Node it)const{
  46. return t==it.t?P[0]==it.P[0]?P[1]==it.P[1]?0:P[1]<it.P[1]:P[0]<it.P[0]:t<it.t;
  47. }
  48. inline Node Hu()const{
  49. Node x;x.t=-1;return x;
  50. }
  51. inline bool CheckHu()const{
  52. return!~t||t>=7||P[1].CheckHu();
  53. }
  54. inline Node operator+(const ui&x){
  55. if(CheckHu())return Hu();Node ans;
  56. ans.P[0].Update(P[0],x);ans.P[1].Update(P[1],x);ans.t=t;
  57. if(x>=2)ans.P[1].Update(P[0],x-2),++ans.t;
  58. if(ans.CheckHu())ans=Hu();
  59. return ans;
  60. }
  61. }tr[2100];
  62. ui trans[2100][5];
  63. inline ui*operator[](const ui&x){
  64. return trans[x];
  65. }
  66. inline Node Hu()const{
  67. return tr[0].Hu();
  68. }
  69. std::map<Node,ui>P;
  70. #define pos(x) (P.count(x)?P[x]:(tr[P[x]=++tot]=x,tot))
  71. inline void Build(){
  72. tr[1].P[0][0][0]=0;P[tr[1]]=++tot;P[tr[2]=Hu()]=++tot;
  73. for(ui i=1;i<=tot;++i)if(i!=2){
  74. for(ui t=0;t<=4;++t)trans[i][t]=pos(tr[i]+t);
  75. }
  76. }
  77. }Hu;
  78. signed main(){
  79. ui ans(0);
  80. Hu.Build();scanf("%u",&n);m=(n<<2)-13;dp[0][0][1]=1;
  81. fac[0]=fac[1]=ifac[0]=ifac[1]=1;
  82. for(ui i=2;i<=m;++i)fac[i]=1ull*fac[i-1]*i%mod,ifac[i]=1ull*(mod-mod/i)*ifac[mod%i]%mod;
  83. for(ui i=2;i<=m;++i)ifac[i]=1ull*ifac[i-1]*ifac[i]%mod;
  84. for(ui i=1;i<=13;++i){
  85. ui w,t;scanf("%u%u",&w,&t);++a[w];
  86. }
  87. for(ui i=0;i<n;++i){
  88. const ui&q=i&1,p=q^1;
  89. for(ui j=0;j<=m;++j)for(ui S=1;S<=Hu.tot;++S)if(dp[q][j][S]){
  90. for(ui x=0;x<=4-a[i+1];++x){
  91. dp[p][j+x][Hu[S][a[i+1]+x]]=(dp[p][j+x][Hu[S][a[i+1]+x]]+1ull*C[4-a[i+1]][x]*dp[q][j][S])%mod;
  92. }
  93. }
  94. for(ui i=0;i<=m;++i)for(ui S=1;S<=Hu.tot;++S)dp[q][i][S]=0;
  95. }
  96. for(ui i=1;i<=m;++i)for(ui S=1;S<=Hu.tot;++S)if(S!=2){
  97. ans=(ans+1ull*fac[i]*fac[m-i]%mod*dp[n&1][i][S])%mod;
  98. }
  99. printf("%u",1ull*ans*ifac[m]%mod+1);
  100. }

LGP5279题解的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

随机推荐

  1. Lua非常有用的工具——递归打印表数据

    摘要: Lua是一种非常小巧的语言.虽小,但五脏俱全. 在Lua中,我认为最最核心的数据结构就是表.表不仅可用作数组,还可以用作字典.Lua面向对象的实现也是用表实现的. 表对于Lua实在是太重要了! ...

  2. 为什么后台给前台Date是时间戳,而前台给后台则直接是时间字符串?

    一.因为时间的格式有很多种,不同的页面可能对不同的时间显示需求不同.比如: 05-8-8 上午9:17 2005-8-8 9:17:42 2005年8月8日 上午09时17分42秒 2005年8月8日 ...

  3. 【转载】Nginx简介及使用Nginx实现负载均衡的原理

    原文地址:http://blog.csdn.net/u014749862/article/details/50522276 是什么? Nginx 这个轻量级.高性能的 web server 主要可以干 ...

  4. 纯JS脚本发送HTTP请求

    1 var xmlHttp; 2 var iii = 0; 3 if (window.XMLHttpRequest) { 4 xmlHttp = new XMLHttpRequest(); 5 if ...

  5. Java判断是否是回文字符串

    public static boolean isPalindrome(String str) { int start = 0, end = str.length() - 1; while (start ...

  6. Solution -「FJWC 2020」人生

    \(\mathcal{Description}\)   OurOJ.   有 \(n\) 个结点,一些结点有染有黑色或白色,其余待染色.将 \(n\) 个结点染上颜色并连接有向边,求有多少个不同(结点 ...

  7. Solution -「国家集训队」「洛谷 P4451」整数的 lqp 拆分

    \(\mathcal{Description}\)   Link.   求 \[\sum_{m>0\\a_{1..m}>0\\a_1+\cdots+a_m=n}\prod_{i=1}^mf ...

  8. ctf linux 基本命令

    从网站上下载一个tar.gz的安装包,在linux下解压缩. 获得一个flag的二进制文件. 通过linux的grep命令搜索到key. grep命令参数中文说明

  9. 三大数据库 sequence 之华山论剑 (上篇)

    前言 本文将基于以下三种关系型数据库,对 sequence (序列) 展开讨论. Oracle - 应用最广泛的商用关系型数据库 PostgreSQL - 功能最强大的开源关系型数据库 MySQL - ...

  10. ASP.NET Core 6框架揭秘实例演示[13]:日志的基本编程模式[上篇]

    <诊断跟踪的几种基本编程方式>介绍了四种常用的诊断日志框架.其实除了微软提供的这些日志框架,还有很多第三方日志框架可供我们选择,比如Log4Net.NLog和Serilog 等.虽然这些框 ...