传送门

Solution 

对于条件一:记录一个\(cnt\)表示牌个数\(≥2\)的个数

设\(dp_{i,0/1,j,k}\)表示考虑了\(1...i\),当前是否有对子,以\(i-1\),\(i\)开始的顺子数为\(j\),\(k\)个,的最大面子数

其中\(dp_i\)是一个大小为\(18\)的状态,我们通过搜索可以发现这样的状态很少

不胡的状态有\(2091\)个,可以直接开一个\(map\)来记录,这里要记得重载小于号

将期望看成,如果摸了\(i(i≥13)\)张牌,如果仍然不胡则贡献\(1\)

答案需要求出有\(i\)张牌时仍然不胡的方案数,并乘一个排列数\((4n-i)!\)

仍然考虑dp,设\(f_{i,j,k}\)表示考虑\(1...i\),当前状态为\(j\),一共选了\(k\)张牌的方案数

转移时枚举\(i+1\)面值选多少张即可

总复杂度\(O(n^2S)\),\(S\)表示状态数

Code 

#include<bits/stdc++.h>
using namespace std;
#define reg register
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int P=998244353,C[5][5]={{1},{1,1},{1,2,1},{1,3,3,1},{1,4,6,4,1}};
int Mul(int x,int y){return (1ll*x*y)%P;}
int Add(int x,int y){return (x+y)%P;}
int n,w[15],t[105];
void rw(int &x,int y){if(min(y,4)>x)x=y;}
struct State
{
int f[18],cnt;
State(){memset(f,-1,sizeof f);f[0]=cnt=0;}
bool operator <(const State&o)const
{
if(cnt!=o.cnt) return (cnt<o.cnt);
for(int i=0;i<18;++i)
if(f[i]!=o.f[i]) return (f[i]<o.f[i]);
return false;
}
bool HU(){return cnt>=7||f[9]>=4;}
friend State Trans(State a,int b)
{
register int i,j,k;State c;c.cnt=a.cnt+(b>=2);
for(i=0;i<3;++i)for(j=0;j<3;++j)for(k=0;k<3&&i+j+k<=b;++k)
{
if(~a.f[i*3+j]) rw(c.f[j*3+k],a.f[i*3+j]+i+(b-i-j-k)/3);
if(~a.f[i*3+j+9])rw(c.f[9+j*3+k],a.f[9+i*3+j]+i+(b-i-j-k)/3);
if(i+j+k+2<=b&&~a.f[i*3+j]) rw(c.f[9+j*3+k],a.f[i*3+j]+i);
}
return c;
}
}st[2100];int tot;int tr[2100][5];
std::map<State,int> mp;
int dfs(State cur)
{
if(cur.HU()) return 0;
if(mp.find(cur)!=mp.end()) return mp[cur];
st[mp[cur]=++tot]=cur;int now=tot;
for(int i=0;i<=4;++i) tr[now][i]=dfs(Trans(cur,i));
return now;
}
int f[2][2100][405],fac[450],inv[450],ans;
int main()
{
dfs(State());
register int sum,i,j,k,l;
for(inv[0]=inv[1]=fac[0]=fac[1]=1,i=2;i<=420;++i)
fac[i]=Mul(fac[i-1],i),inv[i]=Mul((P-P/i),inv[P%i]);
for(i=2;i<=420;++i) inv[i]=Mul(inv[i],inv[i-1]);
n=read();
for(i=1;i<=13;++i) w[i]=read(),read(),++t[w[i]];
f[0][1][0]=1; for(sum=0,i=1;i<=n;sum+=t[i],++i)
{
memset(f[i&1],0,sizeof f[i&1]);
for(j=1;j<=tot;++j)for(k=sum;k<=(i-1)<<2;++k)if(f[(i&1)^1][j][k])for(l=t[i];l<=4;++l)if(tr[j][l])
{
int nxt=tr[j][l];
f[i&1][nxt][k+l]=Add(f[i&1][nxt][k+l],
Mul(f[(i&1)^1][j][k],Mul(C[4-t[i]][l-t[i]],Mul(inv[k-sum],fac[k+l-sum-t[i]]))));
}
} ans=0;
for(i=0;i<=(n<<2);ans=Add(ans,Mul(l,fac[(n<<2)-i])),++i)
for(l=0,j=1;j<=tot;l=Add(l,f[n&1][j][i]),++j); printf("%d\n",Mul(ans,inv[(n<<2)-13]));
return 0;
}

Blog来自PaperCloud,未经允许,请勿转载,TKS!

「ZJOI2019」麻将的更多相关文章

  1. Loj #3042. 「ZJOI2019」麻将

    Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...

  2. 【LOJ】#3042. 「ZJOI2019」麻将

    LOJ#3042. 「ZJOI2019」麻将 如何判定一个集合牌有没有胡的子集是不是胡的 就用一个\(dp[j][k][0/1]\)表示有j个连续两个的串,有k个连续1个串,有没有对子,再记一下这个集 ...

  3. 「ZJOI2019」&「十二省联考 2019」题解索引

    「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...

  4. LOJ3044. 「ZJOI2019」Minimax 搜索

    LOJ3044. 「ZJOI2019」Minimax 搜索 https://loj.ac/problem/3044 分析: 假设\(w(1)=W\),那么使得这个值变化只会有两三种可能,比\(W\)小 ...

  5. Loj #3044. 「ZJOI2019」Minimax 搜索

    Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...

  6. Loj #3045. 「ZJOI2019」开关

    Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...

  7. 【LOJ】#3046. 「ZJOI2019」语言

    LOJ#3046. 「ZJOI2019」语言 先orz zsy吧 有一个\(n\log^3n\)的做法是把树链剖分后,形成logn个区间,这些区间两两搭配可以获得一个矩形,求矩形面积并 然后就是对于一 ...

  8. 【LOJ】#3044. 「ZJOI2019」Minimax 搜索

    LOJ#3044. 「ZJOI2019」Minimax 搜索 一个菜鸡的50pts暴力 设\(dp[u][j]\)表示\(u\)用\(j\)次操作能使得\(u\)的大小改变的方案数 设每个点的初始答案 ...

  9. 【LOJ】#3043. 「ZJOI2019」线段树

    LOJ#3043. 「ZJOI2019」线段树 计数转期望的一道好题-- 每个点设两个变量\(p,q\)表示这个点有\(p\)的概率有标记,有\(q\)的概率到祖先的路径上有个标记 被覆盖的点$0.5 ...

随机推荐

  1. Object类的toString()和equals()方法

    我们知道,Object类是所有类的父类,因此也被称为根类.祖先.那么,我们就来看一看Object类的最常用的两个方法是如何用的. 1.toString方法: Object类的toString()方法默 ...

  2. Python进阶----异常处理

    Python进阶----异常处理 一丶错误和异常   错误:       语法错误(这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正) #语法错误示范一 if #语法错误示范二 ...

  3. Docker 基础篇 入门篇

    1.Docker入门 1.为什么要用docker? 相比于传统: 部署非常慢 成本非常高 资源浪费 难于迁移和扩展 可能会被限定硬件厂商 由于物理机的诸多问题,后来出现了虚拟机 一个物理机可以部署多个 ...

  4. 基于RBAC模型的权限设计:如何设计系统权限体系?

    一.什么是RABC RBAC(基于角色的权限控制)模型的核心是在用户和权限之间引入了角色的概念.取消了用户和权限的直接关联,改为通过用户关联角色.角色关联权限的方法来间接地赋予用户权限(如下图),从而 ...

  5. 编程风格统一配置EditorConfig

    EditorConfig 以纯原生无需任何插件支持 EditorConfig 代码风格配置的编辑器: Visual Studio 2017 开始添加了对 EditorConfig 的原生支持.Visu ...

  6. 【书评:Oracle查询优化改写】第二章

    [书评:Oracle查询优化改写]第二章 BLOG文档结构图 在上一篇中http://blog.itpub.net/26736162/viewspace-1652985/,我们主要分析了一些单表查询的 ...

  7. Pycharm中连接数据库乱码问题解决

    当我们使用pycharm建立数据库之后,看到里面的数据都是乱码,就像下面一样: 其实这个并不是pycharm的显示问题,而是建立数据库时产生的. 解决方法是到指定字符集的命令提示符中重新建表并指定字符 ...

  8. Deprecated: Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version. To avoid this warning set 'always_populate_raw_post_data' to '-1' in php.ini and use the

    参考链接 解决方法: 修改 php.ini  : always_populate_raw_post_data = -1 PHP 5.6已经废弃了$HTTP_RAW_POST_DATA

  9. uboot中的mmc命令(转)

    转载地址:https://blog.csdn.net/a624731186/article/details/37700205 一:mmc的命令如下: 1:对mmc读操作 mmc read addr b ...

  10. k8s之volume

    pause容器为基础架构容器,每一个节点都有一个pause镜像, 为每一个pod提供底层基础支撑设备,所有pod中容器会共享此容器的网络空间,存储卷也是 还可使用csi,存储插件. 使用存储卷步骤1. ...