题目链接: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;
}