zoukankan      html  css  js  c++  java
  • 1117考试T4

    1117考试总结T4

    ​ 题目大意:

    ​ 锦标赛游戏的规则是这样的:一共有 i(1≤i≤n)个人参与游戏,每个人都编上号(之后用 编号代替人)。任意两个人之间都要进行一场比赛(即单循环赛制),每一场比赛双方获胜的 概率都是 0.5。对于两个人 x 和 y(1≤x,y≤i),如果 x=y 或存在一个序列(a 1 ,a 2 ,...,a m )(m≥2),满足 a 1 战胜了 a 2 ,a 2 战胜了 a 3 ,...,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) 元奖金。奖金最多的人获胜。
    ​ 算出对于每一个 i,所有编号的期望奖金的最大值是多少。这个数字可能不是有限小数,所以你需要求的是答案 mod 998244353的结果。(n <= 3000)

    ​ DP + 期望 + 图论知识.

    ​ 首先我们知道这可以连成一张完全图, 因为每两个人之间都要进行一场比赛.又因为比赛有正有负, 并且概率是0.5, 所以我们可以等概率的给每条边标上正反两个方向, 形成了一个有向完全图, 也叫作竞赛图.会等概率的形成(displaystyle 2 ^ { frac{n(n - 1)}{2}})个竞赛图.

    ​ 我们又可以发现, 每个人的期望得分是相同的.所以答案可以这么算:总价值/情况总数.

    (a_i)表示"这个图是一个强联通分量并且这个图包含(i)个点"这种图的个数, 然后我们可以列出一个等式:

    (displaystyle 2^{frac{n(n - 1)}{2}} = sum_{i = 1}^{n}C_n^ia_i2^{frac{(n - i)(n - i - 1)}{2}}).

    ​ 等式右边解释一下:(i)枚举的是这个(n)个点的竞赛图缩点后, (topo)排序后剩下的最后一个点的大小,(这个点是由几个强联通点缩点后形成的).然后我们从(i)个点中任选(i)个点作为最后的这个点, 剩下(n - i)个点随便连边.那么枚举的这(i)个点与剩下(n - i)个点之间的那些边为什么没有考虑呢?因为根据(i)的定义, 这(i)个点肯定是最后剩下的那些点, 缩点后是不能有出边的, 否则它将不是最后剩下的那个点, 所以说中间的那些边是已经确定方向的, 不用考虑.

    ​ 我们把上式的(a_n)提到等式的一边:(a_n = displaystyle 2^{frac{n(n - 1)}{2}} - sum_{i = 1}^{n - 1}C_n^ia_i2^{frac{(n - i)(n - i - 1)}{2}}),然后我们就可以(O(n ^ 2))的计算(a)数组了.

    ​ 为什么要计算(a)数组呢?因为我们要计算(f)数组.

    (f_i)表示有(i)个人参与游戏时的总价值是多少, 然后我们可以写出:

    (f_n = displaystyle sum_{i = 1}^{n} C_n^ia_i(f_{n - i} + i*d_i * 2^{frac{(n - i)(n - i - 1)}{2}})).

    ​ 等式右边解释一下:(i)的含义和上面一样, 也是这个(n)个点的竞赛图缩点后, (topo)排序后最后剩下的那个点的大小.还是从(n)个点里面任选(i)个.然后还剩下(n - i)个点, 这(n - i)个点随便连边的情况数乘上每一种情况都将有(i)个人每人拿到(d_i)元奖金.

    ​ 最后的答案就是:(displaystyle ans_i = frac{f_i}{2^{frac{i(i - 1)}{2}} *i}).(就是上面那个总价值/情况总数, 另外还要把总价镇平分到每个人身上, 也就是在除(i)).

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
    	long long s = 0, f = 1; char ch;
    	while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    	for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    	return s * f;
    }
    
    const int N = 3005, mod = 998244353;
    int n;
    int d[N], a[N], f[N], C[N][N], pow_2[N * N];
    
    void make_pre() {
    	pow_2[0] = 1;
    	for(int i = 1;i < N * N; i++) pow_2[i] = 1ll * pow_2[i - 1] * 2 % mod;
    	for(int i = 0;i < N; i++) C[i][0] = 1;
    	for(int i = 1;i < N; i++) 
    		for(int j = 1;j < N; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
    
    int ksm(long long x, int y) {
    	int res = 1;
    	while(y) { if(y & 1) res = 1ll * res * x % mod; x = x * x % mod; y >>= 1; };
    	return res;
    }
    
    int main() {
    
    	n = read(); make_pre();
    	for(int i = 1;i <= n; i++) d[i] = read();
    	for(int i = 1;i <= n; i++) {
    		a[i] = pow_2[i * (i - 1) / 2];
    		for(int j = 1;j <= i - 1; j++) 
    			a[i] = ((a[i] - 1ll * pow_2[(i - j) * (i - j - 1) / 2] * C[i][j] % mod * a[j] % mod) % mod + mod) % mod;			
    	}
    	for(int i = 1;i <= n; i++) 
    		for(int j = 1;j <= i; j++)
    			f[i] = (f[i] + 1ll * C[i][j] * a[j] % mod * ((f[i - j] + 1ll * j * d[j] % mod * pow_2[(i - j) * (i - j - 1) / 2] % mod) % mod) % mod) % mod;
    	for(int i = 1;i <= n; i++) 
    		printf("%lld
    ", 1ll * ksm(1ll * i * pow_2[i * (i - 1) / 2] % mod, mod - 2) * f[i] % mod);
    	
    	return 0;
    }
    
  • 相关阅读:
    java数组------数组基本使用和3中初始化方式
    java面向对象-------final关键字
    java面向对象------- 多态
    java面向对象------- 封装
    Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
    Android 音视频开发(四):使用 Camera API 采集视频数据
    音频 PCM 数据的采集和播放
    http协议的学习
    Kotlin入门学习笔记
    RxJava笔记
  • 原文地址:https://www.cnblogs.com/czhui666/p/13997713.html
Copyright © 2011-2022 走看看