锦标赛游戏 解题报告

锦标赛游戏

问题描述

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

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

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

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

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

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

原文地址:https://www.cnblogs.com/butterflydew/p/9871771.html