锦标赛游戏
问题描述
( t{YJC}) 很喜欢玩游戏, 今天他决定和朋友们玩锦标赛游戏。
锦标赛游戏的规则是这样的: 一共有 (i(1≤i≤n))个人参与游戏, 每个人都编上号( 之后用编号代替人)。
任意两个人之间都要进行一场比赛( 即单循环赛制), 每一场比赛双方获胜的概率都是 (0.5)。 对于两个人 (x) 和 (y) ((1≤x,y≤i)), 如果 (x=y) 或存在一个序列((a_1,a_2,dots,a_m)(m≥2)),满足 (a_1) 战胜了 (a_2), (a_2) 战胜了 (a_3), (dots), (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) 元奖金。 奖金最多的人获胜。
( t{YJC}) 很想赢得游戏, 但他太笨了, 他想让你帮他算出对于每一个 (i), 所有编号的期望奖金的最大值是多少。 这个数字可能不是有限小数, 所以你需要求的是答案 (mod 998244353)的结果。
输入格式
第一行包含一个整数 (n), 表示最大人数。
接下来 (n) 行, 第((i+1))行包含一个整数 (d_i), 表示有 (i) 个人实力相当时获得的奖金。
输出格式
输出 (n) 行, 第 (i) 行包含一个整数, 表示 (i) 个人参与游戏时所有编号的期望奖金的最大值(mod 998244353) 的结果。
数据说明
对于 (30\%) 的数据, 满足 (n≤7)。
对于 (100\%) 的数据, 满足 (n≤3000)。
内什么,暴力的复杂度是(O(n^22^{frac{n(n-1)}{2}}))的
就是枚举边的方向求一下联通分量的大小就好了,考场上复杂度算错了就没打...没打,亏了(30pts)
正解有点厉害。
发现每个点的期望是一样的,我们不妨求出每个大小的图的总贡献,然后除以情况和点的数量。
令(a_i)代表(i)个点组成竞赛图时形成强连通分量的图的个数。
那么对于一个大小为(n)的竞赛图,我们有
左边代表边的总情况,右边我们枚举缩点( t{topo})排序后最后一个新点的大小。
右边的情况分别为,其他点的任意连边集合,选择组成新点的情况,组成强连通点的情况。
注意其他点向强连通点的连边只有一种方向,所以方案是(1)
这样的话我们可以得出(a_n)的一个递推式
(a_n=2^{frac{n(n-1)}{2}}-sum_{i=1}^{n-1}2^{frac{(n-i-1)(n-i)}{2}}inom{n}{i}a_i)
设(f_i)代表(i)个点组成的图的所有情况的贡献和,则有
理解起来和上面差不多吧。
直接实现递推是(O(n^2))的,据说可以用( t{NTT})优化到(O(nlogn))
Code:
#include <cstdio>
#define ll long long
const ll mod=998244353ll;
const int N=3000;
ll po[N*N+10],C[N+10][N+10],a[N+10],f[N+10],d[N+10];
int n;
void init()
{
po[0]=C[0][0]=1;
for(int i=1;i<=N;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
for(int i=1;i<=N*N;i++) po[i]=po[i-1]*2%mod;
}
ll quickpow(ll d,ll k)
{
ll f=1;
while(k)
{
if(k&1) f=f*d%mod;
d=d*d%mod;
k>>=1;
}
return f;
}
int main()
{
init();
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",d+i);
a[1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
(a[i]-=po[(i-j)*(i-j-1)/2]*C[i][j]%mod*a[j])%=mod;
(a[i]+=po[i*(i-1)/2])%=mod;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
(f[i]+=C[i][j]*a[j]%mod*(f[i-j]+d[j]*po[(i-j)*(i-j-1)/2]%mod*j%mod))%=mod;
f[i]=(f[i]+mod)%mod;
}
for(int i=1;i<=n;i++)
{
ll p=quickpow(po[(i*(i-1)/2)]*i%mod,mod-2);
printf("%lld
",f[i]*p%mod);
}
return 0;
}
2018.10.29