[ZJOI2019]麻将
题目传送门
突然想起某个神仙教了我这道神仙题qwq那就巩固一下吧
题意简述
-
你有 \(13\) 张牌,还有 \(4n-13\) 张牌未摸。
-
对于未摸的所有排列,定义 \(P\) 为最小的 \(i\) 满足 \(S_i\) 存在一个子集是胡的。
-
求 \(P\) 的期望。
dp of dp
先考虑我们最后需要什么。
假设我们知道 \(g_i\) 表示摸了 \(i\) 张牌后不胡的方案数。
那么我们可以将答案转化成下式。
\(\dfrac{\sum_{i=1}^{4n-13}g_i*i!(4n-13-i)!}{(4n-13)!}+1\)
因此,我们将答案转化为摸了 \(i\) 张牌不胡的方案数。
如何判断一副牌胡没胡呢(看出来)。
我们首先可以把相同的三个顺子视为三个刻子,这对结果没有影响。
那我们可以认为,相同的顺子最多只有两个。
先不考虑七对子的情况。
我们设 \(f_{i,k,l,x}\) 表示只考虑 \([1,i]\) 之间的麻将,从 \(i-1\) 开始的刻子有 \(k\) 张,从 \(i\) 开始的刻子有 \(l\) 张,是否已经选出了一个对子(即 \(x=0/1\)),最多能组成多少刻子。
那么能胡牌的条件就是 \(f_n,k,l,1>3\)。
七对子易特判。
所以把 \(18\) 维的状态全压进dp里就好啦。
令 \(dp_{i,j,k,S}\) 表示用了 \([1,i]\),一共选了 \(j\) 张牌,用 \(k\) 种牌可以组成对子,且18维dp的状态是 \(S\) 的方案数。
这个 \(S\) 看起来好大怎么办qaq,可以考虑提前搜出所有可能的 \(S\)。
我们可以把所有合法的状态统一为状态 \(T\)。
暴力发现不胡的状态**不超过 \(3000\) **,可以直接莽dpqwq。
附上代码qwq。
/*
***
还要继续努力
成为一名烤咕学家哦
***
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Mo=998244353;
ll m,n,p,nxt[3005][5],tot,vis[105],C[5][5],fac[505],inv[505],dp[105][405][4005];
map<vector<ll>,ll>mp;
template <typename T> void rd(T &x){
ll fl=1;x=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') fl=-fl;
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
x*=fl;
}
void wr(ll x){
if(x<0) x=-x,putchar('-');
if(x<10) putchar(x+'0');
if(x>9) wr(x/10),putchar(x%10+'0');
}
inline void Max(ll &x,ll y){x=max(x,y);}
ll dfs(vector<ll> nw){
if(mp.find(nw)!=mp.end()) return mp[nw];
mp[nw]=++tot;
ll ID=tot;
for(ll i=0;i<=4;++i){
vector<ll> temp; temp.clear();
for(ll j=0;j<19;++j) temp.push_back(-1);
temp[18]=nw[18]+(i>=2);
for(ll a=0;a<3;++a) for(ll b=0;b<3;++b) for(ll k=0;k<3&&a+b+k<=i;++k){
if(nw[a+b*3]!=-1) Max(temp[b+k*3],nw[a+b*3]+a+(i-a-b-k)/3);
if(nw[a+b*3+9]!=-1) Max(temp[b+k*3+9],nw[a+b*3+9]+a+(i-a-b-k)/3);
if(a+b+k+2<=i&&nw[a+b*3]!=-1) Max(temp[b+k*3+9],nw[a+b*3]+a);
}
for(ll j=0;j<18;++j) temp[j]=min(temp[j],4ll);
if(temp[9]>=4||temp[18]>=7) nxt[ID][i]=0;
else nxt[ID][i]=dfs(temp);
}
return ID;
}
ll pre(){
vector<ll> nw;
for(ll i=0;i<18;++i) nw.push_back(-1); nw.push_back(0);
nw[0]=0;
return dfs(nw);
}
ll pw(ll x,ll y){
ll s=1;
while(y){
if(y&1) s=1ll*s*x%Mo;
x=1ll*x*x%Mo;y>>=1;
}
return s;
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
dp[0][0][pre()]=1;
rd(n);
for(ll i=0;i<=4;++i){
C[i][0]=1;
for(ll j=1;j<=i;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mo;
}
fac[0]=1;
for(ll i=1;i<505;++i) fac[i]=1ll*fac[i-1]*i%Mo;
for(ll i=0;i<505;++i) inv[i]=pw(fac[i],Mo-2);
for(ll i=1,x,y;i<=13;++i){
rd(x),rd(y);
vis[x]++;
}
ll ans=0;
for(ll i=1;i<=n;++i)
for(ll j=0;j<=4*(i-1);++j)
for(ll k=1;k<=tot;++k)
if(dp[i-1][j][k])
for(ll l=0;l<=4-vis[i];++l)
(dp[i][j+l][nxt[k][vis[i]+l]]+=1ll*dp[i-1][j][k]*C[4-vis[i]][l]%Mo)%=Mo;
for(ll j=0;j<=4*n-13;++j)
for(ll k=1;k<=tot;++k)
if(dp[n][j][k]) (ans+=1ll*fac[j]*fac[4*n-13-j]%Mo*dp[n][j][k]%Mo)%=Mo;
wr(1ll*ans*inv[4*n-13]%Mo);puts("");
return 0;
}
欢迎碾压qaq。
有没有大神带我玩awa。