1117考试总结T4
题目大意:
锦标赛游戏的规则是这样的:一共有 i(1≤i≤n)个人参与游戏,每个人都编上号(之后用 编号代替人)。任意两个人之间都要进行一场比赛(即单循环赛制),每一场比赛双方获胜的 概率都是 0.5。对于两个人 x 和 y(1≤x,y≤i),如果 x=y 或存在一个序列(a 1 ,a 2 ,...,a m )(m≥2),满足 a 1 战胜了 a 2 ,a 2 战胜了 a 3 ,...,a m-1 战胜了 a m ,且 a 1 =x,a m =y,则称 x 不弱于 y。如果 x 不弱于 y 且 y 不弱于 x,则称 x 和 y 是实力相当的。比赛结束后会给每个人发奖金。如果某个人 j(1≤j≤i)有 k(1≤k≤n)个人和他实力相当,则给他发 (d_k) 元奖金。奖金最多的人获胜。
算出对于每一个 i,所有编号的期望奖金的最大值是多少。这个数字可能不是有限小数,所以你需要求的是答案 mod 998244353的结果。(n <= 3000)
DP + 期望 + 图论知识.
首先我们知道这可以连成一张完全图, 因为每两个人之间都要进行一场比赛.又因为比赛有正有负, 并且概率是0.5, 所以我们可以等概率的给每条边标上正反两个方向, 形成了一个有向完全图, 也叫作竞赛图.会等概率的形成(displaystyle 2 ^ { frac{n(n - 1)}{2}})个竞赛图.
我们又可以发现, 每个人的期望得分是相同的.所以答案可以这么算:总价值/情况总数.
(a_i)表示"这个图是一个强联通分量并且这个图包含(i)个点"这种图的个数, 然后我们可以列出一个等式:
(displaystyle 2^{frac{n(n - 1)}{2}} = sum_{i = 1}^{n}C_n^ia_i2^{frac{(n - i)(n - i - 1)}{2}}).
等式右边解释一下:(i)枚举的是这个(n)个点的竞赛图缩点后, (topo)排序后剩下的最后一个点的大小,(这个点是由几个强联通点缩点后形成的).然后我们从(i)个点中任选(i)个点作为最后的这个点, 剩下(n - i)个点随便连边.那么枚举的这(i)个点与剩下(n - i)个点之间的那些边为什么没有考虑呢?因为根据(i)的定义, 这(i)个点肯定是最后剩下的那些点, 缩点后是不能有出边的, 否则它将不是最后剩下的那个点, 所以说中间的那些边是已经确定方向的, 不用考虑.
我们把上式的(a_n)提到等式的一边:(a_n = displaystyle 2^{frac{n(n - 1)}{2}} - sum_{i = 1}^{n - 1}C_n^ia_i2^{frac{(n - i)(n - i - 1)}{2}}),然后我们就可以(O(n ^ 2))的计算(a)数组了.
为什么要计算(a)数组呢?因为我们要计算(f)数组.
(f_i)表示有(i)个人参与游戏时的总价值是多少, 然后我们可以写出:
(f_n = displaystyle sum_{i = 1}^{n} C_n^ia_i(f_{n - i} + i*d_i * 2^{frac{(n - i)(n - i - 1)}{2}})).
等式右边解释一下:(i)的含义和上面一样, 也是这个(n)个点的竞赛图缩点后, (topo)排序后最后剩下的那个点的大小.还是从(n)个点里面任选(i)个.然后还剩下(n - i)个点, 这(n - i)个点随便连边的情况数乘上每一种情况都将有(i)个人每人拿到(d_i)元奖金.
最后的答案就是:(displaystyle ans_i = frac{f_i}{2^{frac{i(i - 1)}{2}} *i}).(就是上面那个总价值/情况总数, 另外还要把总价镇平分到每个人身上, 也就是在除(i)).
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 3005, mod = 998244353;
int n;
int d[N], a[N], f[N], C[N][N], pow_2[N * N];
void make_pre() {
pow_2[0] = 1;
for(int i = 1;i < N * N; i++) pow_2[i] = 1ll * pow_2[i - 1] * 2 % mod;
for(int i = 0;i < N; i++) C[i][0] = 1;
for(int i = 1;i < N; i++)
for(int j = 1;j < N; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
int ksm(long long x, int y) {
int res = 1;
while(y) { if(y & 1) res = 1ll * res * x % mod; x = x * x % mod; y >>= 1; };
return res;
}
int main() {
n = read(); make_pre();
for(int i = 1;i <= n; i++) d[i] = read();
for(int i = 1;i <= n; i++) {
a[i] = pow_2[i * (i - 1) / 2];
for(int j = 1;j <= i - 1; j++)
a[i] = ((a[i] - 1ll * pow_2[(i - j) * (i - j - 1) / 2] * C[i][j] % mod * a[j] % mod) % mod + mod) % mod;
}
for(int i = 1;i <= n; i++)
for(int j = 1;j <= i; j++)
f[i] = (f[i] + 1ll * C[i][j] * a[j] % mod * ((f[i - j] + 1ll * j * d[j] % mod * pow_2[(i - j) * (i - j - 1) / 2] % mod) % mod) % mod) % mod;
for(int i = 1;i <= n; i++)
printf("%lld
", 1ll * ksm(1ll * i * pow_2[i * (i - 1) / 2] % mod, mod - 2) * f[i] % mod);
return 0;
}