Codeforces 1765C. Card Guessing
题目链接:C - Card Guessing
题目大意:桌面上有一堆牌,牌有四种花色,每种花色有 \(n\) 张,一共 \(4n\) 张,每次随机从牌堆中抽一张卡直到抽完,抽出来的卡片组成的序列一共有 \((4n)!\) 种。Monocarp 在执行抽卡的过程中会根据过去 \(k\) 次抽卡的结果来猜测本次抽出来的卡片花色,他固定会猜最近这 \(k\) 次结果中出现次数最少的那一个花色(如有多个花色满足条件则随机猜一个,如果之前已经抽卡的数量小于 \(k\) 则猜测依据就是之前所有的抽卡结果),问期望猜对次数。
首先根据期望线性性,期望猜对次数就等于每次猜对概率之和。考虑当前是第 \(i\) 次猜测,设 \(K=\min(k,i-1)\),那么当前决策以及是否猜对只取决于抽卡序列的第 \(i-K\) 至第 \(i\) 项,于是我们只需要考虑一个长度为 \(K+1\) 的序列满足其最后一项与对应猜测结果相同的概率。为了计数方便,我们把相同花色的 \(n\) 张牌当作互不相同的来处理。
根据这一思路,我们很容易想到设 \(g_{i,j}\) 表示长度为 \(i\),花色最少出现次数为 \(j\) 的序列个数。但是这个方案数是很难直接计算的,因为每个花色的出现次数有个上限,不能直接组合数计算,于是考虑使用背包来计数。
设 \(f_{col,num,mn}\) 表示放置了前 \(col\) 个花色,卡牌总数为 \(num\),最少出现次数为 \(mn\) 的方案数。那么对于每个状态,我们只需枚举下一个颜色的放置个数 \(i\),就能够产生从 \(f_{col,num,mn}\) 到 \(f_{col+1,num+i,\min(mn,i)}\) 的转移。由于我们是把同一花色的牌是看作互不相同的,那么转移系数就是 \(\binom{num+i}{i}\cdot \binom{n}{i}\cdot i!\)。背包结束后对于 \(j\ge 1\) 就有 \(g_{i,j}=f_{4,i,j}\),而 \(g_{i,0}\) 则等于 \(\sum_{c=1}^{3}\sum_j\binom{4}{c}f_{c,i,j}\)。除此之外为了方便计数我们还需要记 \(h_i\) 表示长度为 \(i\) 的不同抽卡序列个数,显然就有 \(h_i=\sum_j g_{i,j}\)。
最后计算答案时,我们只需要先求出对应的 \(K\),然后计算满足猜测的序列个数,除以总方案数就能得出对应的概率,当 \(i=1\) 时提前钦定对应概率为 \(\frac{1}{4}\) 即可。最后答案为 \(\frac{1}{4}+\sum_{i=2}^{4n}\frac{\sum_j g_{K,j}\cdot (n-j)}{h_{K+1}}\),其中 \(K=\min(i-1,k)\)。
时间复杂度 \(O(n^3)\)。
#include<bits/stdc++.h>
using namespace std;
#define N 2020
#define LL long long
#define MOD 998244353
int n,k,f[5][N][N>>2],g[N][N>>2],C[N][N],p[N],q[N],inv[N],h[N],ans;
LL qow(LL x,LL y){return y?(y&1?x*qow(x,y-1)%MOD:qow(x*x%MOD,y/2)):1;}
int main()
{
inv[1]=p[0]=q[0]=p[1]=q[1]=1;
C[0][0]=C[1][0]=C[1][1]=1;
for(int i=2;i<N;i++){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;
p[i]=1ll*i*p[i-1]%MOD;
q[i]=1ll*inv[i]*q[i-1]%MOD;
}
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)f[1][i][i]=1ll*p[n]*q[n-i]%MOD;
for(int c=1;c<4;c++)
for(int num=c;num<=c*n;num++)
for(int mn=1;mn<=num/c;mn++)if(f[c][num][mn]){
for(int i=1;i<=n;i++){
int tot=1ll*f[c][num][mn]*C[num+i][i]%MOD,Mn=min(mn,i);
tot=1ll*tot*p[n]%MOD*q[n-i]%MOD;
f[c+1][num+i][Mn]=(f[c+1][num+i][Mn]+tot)%MOD;
}
}
for(int i=1;i<=4*n;i++)
for(int j=1;j<=n;j++)
g[i][j]=1ll*f[4][i][j]%MOD;
for(int c=1;c<=3;c++)
for(int i=1;i<=c*n;i++)
for(int j=1;j<=i/c;j++)
g[i][0]=(g[i][0]+1ll*C[4][c]*f[c][i][j])%MOD;
for(int i=1;i<=4*n;i++)
for(int j=0;j<=n;j++)
h[i]=(h[i]+g[i][j])%MOD;
ans=qow(4,MOD-2);
for(int i=2;i<=4*n;i++){
int K=min(i-1,k),tot=0;
for(int j=0;j<=K/4;j++)
tot=(tot+1ll*g[K][j]*(n-j))%MOD;
ans=(ans+qow(h[K+1],MOD-2)*tot)%MOD;
}
printf("%d\n",ans);
}