锦标赛游戏 解题报告

锦标赛游戏

问题描述

\(\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\)的竞赛图,我们有

\[2^{\frac{n(n-1)}{2}}=\sum_{i=1}^n2^{\frac{(n-i-1)(n-i)}{2}}\ \ \ \ \binom{n}{i}a_i\]

左边代表边的总情况,右边我们枚举缩点\(\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\)个点组成的图的所有情况的贡献和,则有

\[f_n=\sum_{i=1}^n\binom{n}{i}a_i(f_{n-i}+id_i2^{\frac{(n-i)(n-i-1)}{2}}\ \ \ )\]

理解起来和上面差不多吧。

直接实现递推是\(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

转载于:https://www.cnblogs.com/butterflydew/p/9871771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值