Problem
Alice 出了 n n n 场比赛,第 i i i 场比赛有 a i a_i ai 道题。
由于是提高组模拟赛,因此最多只会有三题。
Bob 要来切这些题。由于他艺高人胆大,所以他的切题方式比较特别。
Bob 从 1 1 1 到 n n n 这 n n n 个整数中等概率均匀随机生成一个数 i i i,作为他选择的比赛序号。如果第 i i i 场比赛还有 Bob 没切的题,Bob 会切掉一题,并说一声 “ “ “我好菜啊 ” ” ”;否则他切不了题,但是由于他 AK 了点开的这场比赛,所以他同样会说 “ “ “我好菜啊 ” ” ”。
每次进行完上述操作后,Bob 会从随机开始重新进行上述步骤,直到所有题被切完为止。
没多久,Bob 切完了所有的题,而 Alice 已经数不清楚他说了多少遍“我好菜啊”。
由于 Bob 的选择是随机的,可能无法还原真实情况,所以 Alice 只希望知道 Bob 期望说 “ “ “我好菜啊 ” ” ”的次数。
期望就是指无数多次试验下的平均值。
也就是说,Alice 想知道,假设有无限次这样的试验,Bob 平均情况下经过几轮上述步骤能够最早切完所有题。
但是 Alice 不会概率期望,所以现在请你帮帮 Alice 吧。
由于 Bob 尤其喜欢切傅里叶变换的题,因此答案模 17680321 17680321 17680321(傅里叶的生日,是个质数)。
对于 100 % 100\% 100% 的数据, n ≤ 500 n≤500 n≤500。
Solution
很容易看出这是一道期望概率 d p dp dp题吧。
由于每个比赛最多只有 3 3 3 道题,我们设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示还有 1 1 1 道题没做的比赛数为 i i i,还有 2 2 2 道题没做的比赛数为 j j j,还有 3 3 3 道题没做的比赛数为 k k k。
那么状态转移方程就很明确了,即:
f [ i ] [ j ] [ k ] = i n f [ i − 1 ] [ j ] [ k ] + j n f [ i + 1 ] [ j − 1 ] [ k ] + k n f [ i ] [ j + 1 ] [ k − 1 ] + n − i − j − k n f [ i ] [ j ] [ k ] + 1 f[i][j][k]=\frac{i}{n}f[i-1][j][k]+\frac{j}{n}f[i+1][j-1][k]+\frac{k}{n}f[i][j+1][k-1]+\frac{n-i-j-k}{n}f[i][j][k]+1 f[i][j][k]=nif[i−1][j][k]+njf[i+1][j−1][k]+nkf[i][j+1][k−1]+nn−i−j−kf[i][j][k]+1
把右边的 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 移到左边,再化一下简,得:
f [ i ] [ j ] [ k ] = i i + j + k f [ i − 1 ] [ j ] [ k ] + j i + j + k f [ i + 1 ] [ j − 1 ] [ k ] + k i + j + k f [ i ] [ j + 1 ] [ k − 1 ] + n i + j + k f[i][j][k]=\frac{i}{i+j+k}f[i-1][j][k]+\frac{j}{i+j+k}f[i+1][j-1][k]+\frac{k}{i+j+k}f[i][j+1][k-1]+\frac{n}{i+j+k} f[i][j][k]=i+j+kif[i−1][j][k]+i+j+kjf[i+1][j−1][k]+i+j+kkf[i][j+1][k−1]+i+j+kn
然后按照这个式子转移就可以了。
有一个要注意的地方,即此题的空间限制较小, O ( n 3 ) O(n^3) O(n3) 的空间开不下,用滚动数组优化一维即可。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 505
#define Mod 17680321
using namespace std;
int cnt[5],inv[N],f[N][N][2];
int main(){
int n,x,t=0;
scanf("%d",&n);
inv[1]=1;
for(int i=2;i<=n;++i) inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
for(int i=1;i<=n;++i) scanf("%d",&x),cnt[x]++;
f[0][0][0]=0;
for(int k=0;k<=n;++k){
for(int j=0;j+k<=n;++j){
for(int i=0;i+j+k<=n;++i){
if(i||j||k){
f[i][j][t]=1ll*n*inv[i+j+k]%Mod;
f[i][j][t]=(f[i][j][t]+1ll*i*f[i-1][j][t]%Mod*inv[i+j+k]%Mod)%Mod;
f[i][j][t]=(f[i][j][t]+1ll*j*f[i+1][j-1][t]%Mod*inv[i+j+k]%Mod)%Mod;
f[i][j][t]=(f[i][j][t]+1ll*k*f[i][j+1][t^1]%Mod*inv[i+j+k]%Mod)%Mod;
if(i==cnt[1]&&j==cnt[2]&&k==cnt[3]) return printf("%d",f[i][j][t]),0;
}
}
}
t^=1;
}
return 0;
}