• codeforces 1556F


    题目链接:https://codeforces.com/contest/1556/problem/F

    可以证明,一定存在胜利者,且胜利者们在同一个强连通分量中(反证法),所以考虑枚举胜利者

    (dp[winners]) 表示胜利者为 (winners) 的概率,则期望为 (sum_{winners}dp[winners] imes|winners|)

    由胜利者都在同一个强连通分量中的性质可知,所有胜利者都可以击败每一个非胜利者,否则与强连通分量矛盾

    那么计算 (dp[winners]) 可以考虑容斥,即枚举的 (winners) 可以击败所有非胜利者的情况中,减去胜利者为 (winners) 子集 (sub),同时 (winnerssub) 中的参赛者要击败所有其它参赛者的情况

    时间复杂度 (O(n^23^n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 17;
    const int M = 1000000007;
    
    int n;
    int a[maxn], inv[2000010];
    int dp[1<<maxn], w[maxn][maxn], in[maxn];
    
    int qsm(int i, int po){
    	int res = 1;
    	while(po){
    		if(po&1) res = 1ll * res * i % M;
    		i = 1ll * i * i % M;
    		po >>= 1;
    	}
    	return res;
    }
    
    int count(int s){
    	int tmp = s;
    	int cnt = 0;
    	while(tmp){
    		if(tmp & 1) ++cnt;
    		tmp >>= 1;
    	}
    	return cnt;
    }
    
    int calc(int s){
    	memset(in, 0, sizeof(in));
    	for(int i = 0 ; i < n ; ++i){ if((s>>i) & 1) in[i] = 1; }
    	
    	int G = 1;
    	for(int i = 0 ; i < n ; ++i) {
    		if(!in[i]) continue;
    		for(int j = 0 ; j < n ; ++j){
    			if(in[j]) continue;
    			G = 1ll * G * w[i][j] % M;
    		}
    	} 
    	
    	for(int s0 = s ; s0 ; s0 = (s0-1)&s){
    		if(s == s0) continue;
    		int g = 1;
    		int r = (s^s0); 
    		for(int i = 0 ; i < n ; ++i){
    			if(!((r>>i)&1)) continue;
    			for(int j = 0 ; j < n ; ++j){
    				if(in[j]) continue;
    				g = 1ll * g * w[i][j] % M;
    			}
    		}
    		
    		G = ((G - 1ll * dp[s0] * g % M) % M + M) % M;
    	}
    	dp[s] = G;
    	
    	return dp[s];
    }
    
    ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
    
    int main(){
    	n = read();
    	for(int i = 0 ; i < n ; ++i) a[i] = read();
    	
    	for(int i = 0 ; i < n ; ++i){
    		for(int j = 0 ; j < n ; ++j){
    			w[i][j] = 1ll * a[i] * qsm(a[i]+a[j], M-2) % M;
    		}
    	}
    	
    	int ans = 0;
    	for(int i = 1 ; i < (1<<n) ; ++i){
    		ans = (ans + 1ll * calc(i) * count(i) % M) % M;
    	} 
    	
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    LeetCode208 实现 Trie (前缀树)
    LeetCode289 生命游戏(模拟)
    LeetCode560 和为 K 的子数组
    LeetCode673 最长递增子序列的个数
    LeetCode31 下一个排列
    LeetCode926 将字符串翻转到单调递增
    LeetCode135 分发糖果
    LeetCode810 黑板异或游戏
    npm发布和修改详细教程
    不同的子序列问题I
  • 原文地址:https://www.cnblogs.com/tuchen/p/15240541.html
Copyright © 2020-2023  润新知