锦标赛游戏

105 篇文章 0 订阅
92 篇文章 0 订阅

题目描述

YJC 很喜欢玩游戏,今天他决定和朋友们玩锦标赛游戏。
锦标赛游戏的规则是这样的:一共有 i(1≤i≤n)个人参与游戏,每个人都编上号(之后用 编号代替人)。
任意两个人之间都要进行一场比赛(即单循环赛制),每一场比赛双方获胜的 概率都是 0.5。
对于两个人 x 和 y(1≤x,y≤i),如果 x=y 或存在一个序列(a1,a2,…,am)(m≥2), 满足 a1战胜了 a2,a2战胜了 a3,…,am-1战胜了 am,且 a1=x,am=y,则称 x 不弱于 y。
如 果 x 不弱于 y 且 y 不弱于 x,则称 x 和 y 是实力相当的。
比赛结束后会给每个人发奖金。
如果某个人 j(1≤j≤i)有 k(1≤k≤n)个人和他实力相当,则给他发 dk元奖金。
奖金最多的人获胜。
YJC 很想赢得游戏,但他太笨了,他想让你帮他算出对于每一个 i,所有编号的期望奖 金的最大值是多少。
这个数字可能不是有限小数,所以你需要求的是答案 mod 998244353 的结果。
n<=3000

这个,具体思路就是显然任何两个人之间都有孰强孰弱或是实力相当3种情况,那么我们可以从实力最弱的人开始逐次加入实力强一点点的人,最后加到实力最强的人,同时计算方案数,那么转移的时候为了方便计数,我们需要将实力相当的人一起加入,那么这些人对之前已有的人之间的边的方向一定是指向之前已有的人的,只需考虑内部。
又因为内部的人两两实力相当,由题意可知,这几个人一定形成了一个强联通分量,那么我们的问题就只是如何求解n个点的有标号强联通竞赛图的方案数了。
可是这恰恰是个结论。。。。。。
设f[n] 代表n个点的有标号强联通竞赛图个数,
考虑转移,
考虑补集转化,
我们需要用f[0]~f[n-1]求n个点的非强联通图数。
考虑缩点之后是个DAG,
考虑拓扑序,
枚举这n个点中缩点后拓扑排序中最后的点数为i,
然后发现它的贡献是 f[i] * g[n-i] * C(n,i) ( g[i] = 2 ^ C(i,2) 表示i个点的有标号竞赛图数量 )
然后就可以转移了。。。

AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 998244353
#define maxn 3005
using namespace std;
 
int n,fac[maxn],inv[maxn],invf[maxn];
inline int C(int n,int m)
{  
    if(n < 0 || m < 0 || n-m < 0) return 0;
    return 1ll * fac[n] * invf[m] % mod * invf[n-m] % mod; 
}
 
inline int Pow(int base,int k)
{
    int ret =  1;
    for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret = 1ll * ret * base % mod;
    return ret;
}
int dp1[maxn],g[maxn],d[maxn],dp2[maxn];
 
int main()
{
    scanf("%d",&n);
    fac[0] = fac[1] = inv[0] = inv[1] = invf[0] = invf[1] = 1;
    for(int i=2;i<=n;i++) 
        fac[i] = fac[i-1] * 1ll * i % mod,
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
        invf[i] = 1ll * invf[i-1] * inv[i] % mod;
     
    g[0] = g[1] = dp1[1] = 1;
    for(int i=2;i<=n;i++)
    {
        dp1[i] = g[i] = Pow(2,i*(i-1)/2);
        for(int j=1;j<i;j++)
            dp1[i] = (dp1[i] - 1ll * dp1[j] * g[i-j] % mod * C(i,i-j)) % mod;
    }
     
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    dp2[1] = d[1], dp2[0] = 0;
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<i;j++)
        {
            dp2[i] = (dp2[i]
            + 1ll * dp2[j] * dp1[i-j] % mod * C(i,i-j) % mod
            + 1ll * dp1[i-j] * C(i,i-j) % mod * g[j] % mod * d[i-j] %mod * (i-j)) % mod;
        }
    }
     
    for(int i=1;i<=n;i++)
        printf("%lld\n",((1ll * dp2[i] * Pow(g[i],mod-2) % mod * Pow(i,mod-2) % mod)+mod)%mod);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值