• 题解-PKUWC2018 猎人杀


    Problem

    loj2541

    题意概要:给定 (n) 个人的倒霉度 ({w_i}),每回合会有一个人死亡,每个人这回合死亡的概率为 自己的倒霉度/目前所有存活玩家的倒霉度之和,求第 (1) 个人最后一个死亡的概率

    Solution

    (B = sum_{i=2}^nw_i)

    要求 (1) 号最后一个被选中有点不好做,但是求 (1) 号第一个被选中还是比较好做的((frac {w_1}{sum_{i=1}^nw_i})

    至于这两者怎么联系起来,使用 (mathrm {min-max}) 容斥(和 HAOI2015按位或 有点像:前者是求 (1) 号最后一个被选中的概率;后者是求集合内最后一个被选中的期望次数)

    (mathrm{min-max}) 容斥的式子为:

    [max{S}=sum_{Tsubseteq S}(-1)^{|T|-1}min{T} ]

    由于 (1) 为需要求的点,将其从 (S,T) 的定义中刨除,即

    [max{S}=sum_{Tsubseteq S}(-1)^{|T|}min{T} ]

    而同时

    [min{T}=frac {w_1}{w_1+sum_{xin T}w_i} ]

    发现 (min{T}) 最多有 (B) 种,可以考虑计算出每一项的容斥系数再 (O(B)) 计算

    至于计算容斥系数,可使用母函数求得,即求出下面式子的每一项系数:

    [prod_{i=2}^n(1-x^{w_i}) ]

    分治 ntt 求解,每一层母函数度数和为 (B),复杂度 (O(Blog B)),总复杂度 (O(Blog^2B))

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    inline void read(int&x){
    	char ch=getchar();x=0;while(!isdigit(ch))ch=getchar();
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    }
    
    const int N = 200200, T = 37, p = 998244353;
    int stk[T], tp;
    int brr[T][N], L[T];
    int w[N], rev[N];
    
    inline int qm(const int&x) {return x < p ? x : x - p;}
    inline int qpow(int A, int B) {
    	int res = 1; while(B) {
    		if(B&1) res = (ll)res * A%p;
    		A = (ll)A * A%p, B >>= 1;
    	} return res;
    }
    
    void dft(int*a,int n,int sgn) {
    	for(int i=1;i<n;++i) if(i < rev[i]) swap(a[i], a[rev[i]]);
    	for(int i=1;i<n;i<<=1) {
    		int gn = qpow(3, (p-1)/(i<<1));
    		for(int j=0;j<n;j+=(i<<1)) {
    			int g = 1;
    			for(int k=0;k<i;++k,g=(ll)g*gn%p) {
    				int x = a[j+k], y = (ll)g * a[j+k+i]%p;
    				a[j+k] = qm(x + y), a[j+k+i] = qm(x - y+p);
    			}
    		}
    	}
    	if(!sgn) {
    		int iv = qpow(n, p-2); reverse(a+1,a+n);
    		for(int i=0;i<n;++i) a[i] = (ll)a[i] * iv%p;
    	}
    }
    
    void mul(int ai, int bi, int ci) {
    	int*ar = brr[ai], *br = brr[bi], *cr = brr[ci];
    	int n = L[ai], m = L[bi], &t = L[ci];
    	t = n + m; int nn = 1, l = 0;
    	while(nn < t) nn <<= 1, ++ l;
    	for(int i=n;i<nn;++i) ar[i] = 0;
    	for(int i=m;i<nn;++i) br[i] = 0;
    	for(int i=1;i<nn;++i) rev[i] = (rev[i>>1]>>1) | ((i&1) << l-1);
    	dft(ar, nn, 1), dft(br, nn, 1);
    	for(int i=0;i<nn;++i) cr[i] = (ll)ar[i] * br[i]%p;
    	dft(cr, nn, 0);
    }
    
    int binary(int l, int r) {
    	if(l == r) {
    		int id = stk[--tp], *arr = brr[id];
    		for(int i=w[l]<<1;i;--i) arr[i] = 0;
    		arr[0] = 1, arr[w[l]] = p-1, L[id] = w[l]+1;
    		return id;
    	}
    	int mid = l + r >> 1;
    	int ls = binary(l, mid), rs = binary(mid+1, r);
    	int id = stk[--tp]; mul(ls, rs, id);
    	stk[tp++] = ls, stk[tp++] = rs;
    	return id;
    }
    
    int main() {
    	for(int i=0;i<T;++i) stk[tp++] = T-i-1;
    	int n; read(n);
    	for(int i=1;i<=n;++i) read(w[i]);
    	int id = binary(2, n);
    	
    	int ans = 0, *arr = brr[id];
    	for(int i=0;i<L[id];++i)
    		ans = (ans + (ll)arr[i] * qpow(w[1] + i, p-2))%p;
    	ans = (ll)ans * w[1]%p;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    c语言练习17——输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数
    c语言练习16——输入两个正整数m和n,求其最大公约数和最小公倍数
    c语言练习15——条件运算符的嵌套
    c语言练习14——将一个正整数分解质因数
    CentOS下Cassandra集群搭建
    一台linux服务器挂载另外一台linux服务器文件系统
    zabbix分布式监控多网段的部署与实现
    CentOS安装MySQL详解
    vcenter 7.0 安装 vRealize Operations Manager
    Zabbix分布式部署详细
  • 原文地址:https://www.cnblogs.com/penth/p/10886106.html
Copyright © 2020-2023  润新知