传送门

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. 【转载】C#中List集合中Last和LastOrDefault方法的差别

    在C#的List集合操作中,Last方法和LastOrDefault方法都会用来查找集合中最后一个符合条件的元素对象,但Last和LastOrDefault方法还是有差别的,建议使用LastOrDef ...

  2. angular js根据json文件动态生成路由状态

    项目上有一个新需求,就是需要根据json文件动态生成路由状态,查阅了一下资料,现在总结一下发出来: 首先项目用到的是angular的UI-路由,所以必须引入angular.js和angular-ui- ...

  3. CSS样式表及选择器相关内容(一)

    CSS(Cascading Style Sheets)层叠样式表1.CSS级联-CSS层叠:规定在哪个HTML中使用哪个样式. body{ font-size:16px; //body默认字体大小 } ...

  4. sublime 快速编写代码技巧

    在sublime上装了Emmet插件后,我们就可以利用以下技巧快速编写代码 1.自动生成html头文件 html:5 或!:用于HTML5文档类型 html:xt:用于XHTML过渡文档类型 html ...

  5. JAVA基础之DBUtils与连接池

    利用DBUtils进一步简化JDBC数据库的增删改查的代码,同时利用从连接池中接取连接,进而进行简化和减少资源的消耗! 一.DBUtils: 1.DBUtils就是JDBC的简化开发工具包.需要项目导 ...

  6. day 03作业

    目录 作业 简述执行Python程序的两种方式以及他们的优缺点: 简述Python垃圾回收机制: 对于下述代码: 10的引用计数为多少? x对应的变量值257的引用计数为多少? 简述Python小整数 ...

  7. 微信小程序 - 定位功能

    (1) 查看微信小程序文档 大家可以从我截图中可以看到,API中的返回值有纬度和经度,所以我们接下来就是要用到纬度和经度逆地址解析出地址的一些信息. (2)注册腾讯地图开放平台 注册完之后选择WebS ...

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

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

  9. oracle 11g导出少了空表,原因分析

    oracle 11g导出少了空表 使用exp命令的时候,会出现少表的情况,是因为在11g版本中如果一个表里面是空的,为了节省空间,默认是不会给这个表分配空间的,在导出的时候也就不会将空表导出的,自然导 ...

  10. 逆向破解之160个CrackMe —— 007

    CrackMe —— 007 160 CrackMe 是比较适合新手学习逆向破解的CrackMe的一个集合一共160个待逆向破解的程序 CrackMe:它们都是一些公开给别人尝试破解的小程序,制作 c ...