【CF913F】Strongly Connected Tournament
题面
题解
令\(f_i\)表示大小为\(i\)的竞赛图的场数期望,\(g_i\)表示形成大小为\(i\)的\(SCC\)的概率,\(h_{i,j}\)为\(i\)个人打比赛,其中\(j\)个人被剩下\(i-j\)个人打爆的概率。
枚举最后一个\(SCC\)的大小,有
\[f_i=\sum_{j=1}^i g_jh_{i,j}(f_{i-j}+f_i+{j\choose 2}+j(i-j))
\]
发现这个\(f\)会转移到自身,解一下方程就行了,这里不再赘述。
现在考虑\(g_i\)怎么求,考虑构成\(SCC\)的条件,就是对于一个集合\(S\),不能存在一个集合\(T\),集合\(T\)打爆集合\(\complement_S T\),容斥一下得
\[g_i=1-\sum_{j=1}^{i-1}g_j\times h_{i,j}
\]
最后考虑怎么求\(h\),相当于新加入一个点,他可以放到赢或输的集合中,转移类似于杨辉三角:
\[h_{i,j}=h_{i-1,j-1}\times p^{i-j}+h_{i-1,j}\times (1-p)^{j}
\]
然后就可以根据推好的式子就可以算出来答案了。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int Mod = 998244353;
const int MAX_N = 2e3 + 5;
int fpow(int x, int y) {
int res = 1;
while (y) {
if (y & 1) res = 1ll * res * x % Mod;
x = 1ll * x * x % Mod;
y >>= 1;
}
return res;
}
int N, p, p1[MAX_N], p2[MAX_N];
int f[MAX_N], g[MAX_N], h[MAX_N][MAX_N];
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
cin >> N;
int a, b; cin >> a >> b;
p = 1ll * a * fpow(b, Mod - 2) % Mod;
p1[0] = p2[0] = 1;
for (int i = 1; i <= N; i++) {
p1[i] = 1ll * p1[i - 1] * p % Mod;
p2[i] = 1ll * p2[i - 1] * (Mod + 1 - p) % Mod;
}
for (int i = 0; i <= N; i++)
for (int j = h[i][0] = 1; j <= i; j++)
h[i][j] = (1ll * h[i - 1][j - 1] * p1[i - j] + 1ll * h[i - 1][j] * p2[j]) % Mod;
for (int i = 1; i <= N; i++)
for (int j = g[i] = 1; j < i; j++)
g[i] = (g[i] - 1ll * g[j] * h[i][j] % Mod + Mod) % Mod;
for (int i = 2; i <= N; i++) {
int C = i * (i - 1) / 2;
f[i] = 1ll * g[i] * h[i][i] % Mod * C % Mod;
for (int j = 1; j < i; j++) {
C = j * (j - 1) / 2;
f[i] = (f[i] + 1ll * g[j] * h[i][j] % Mod * (1ll * f[i - j] + f[j] + C + (i - j) * j)) % Mod;
}
f[i] = 1ll * f[i] * fpow(1 - 1ll * g[i] * h[i][i] % Mod + Mod, Mod - 2) % Mod;
}
printf("%d\n", f[N]);
return 0;
}