锦标赛游戏 解题报告
锦标赛游戏
问题描述
\(\tt{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\) 元奖金。 奖金最多的人获胜。
\(\tt{YJC}\) 很想赢得游戏, 但他太笨了, 他想让你帮他算出对于每一个 \(i\), 所有编号的期望奖金的最大值是多少。 这个数字可能不是有限小数, 所以你需要求的是答案 \(\bmod 998244353\)的结果。
输入格式
第一行包含一个整数 \(n\), 表示最大人数。
接下来 \(n\) 行, 第\((i+1)\)行包含一个整数 \(d_i\), 表示有 \(i\) 个人实力相当时获得的奖金。
输出格式
输出 \(n\) 行, 第 \(i\) 行包含一个整数, 表示 \(i\) 个人参与游戏时所有编号的期望奖金的最大值\(\bmod 998244353\) 的结果。
数据说明
对于 \(30\%\) 的数据, 满足 \(n≤7\)。
对于 \(100\%\) 的数据, 满足 \(n≤3000\)。
内什么,暴力的复杂度是\(O(n^22^{\frac{n(n-1)}{2}})\)的
就是枚举边的方向求一下联通分量的大小就好了,考场上复杂度算错了就没打...没打,亏了\(30pts\)
正解有点厉害。
发现每个点的期望是一样的,我们不妨求出每个大小的图的总贡献,然后除以情况和点的数量。
令\(a_i\)代表\(i\)个点组成竞赛图时形成强连通分量的图的个数。
那么对于一个大小为\(n\)的竞赛图,我们有
左边代表边的总情况,右边我们枚举缩点\(\tt{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}}\binom{n}{i}a_i\)
设\(f_i\)代表\(i\)个点组成的图的所有情况的贡献和,则有
理解起来和上面差不多吧。
直接实现递推是\(O(n^2)\)的,据说可以用\(\tt{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\n",f[i]*p%mod);
}
return 0;
}
2018.10.29