zoukankan      html  css  js  c++  java
  • 锦标赛游戏 解题报告

    锦标赛游戏

    问题描述

    ( 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

  • 相关阅读:
    jvm 优化
    SqlServer体系结构
    sqlserver2012 在视图中建索引
    win10 桌面设置为远程桌面
    ORACLE 查询某表中的某个字段的类型,是否为空,是否有默认值等
    activemq读取剩余消息队列中消息的数量
    Oracl 一条sql语句 批量添加、修改数据
    ClickOnce一项Winform部署
    C#语言中的修饰符
    关于MySQL集群的一些看法
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9871771.html
Copyright © 2011-2022 走看看