codeforces F - Sports Betting (概率dp)
阿新 • • 發佈:2021-09-08
題目連結:https://codeforces.com/contest/1556/problem/F
可以證明,一定存在勝利者,且勝利者們在同一個強連通分量中(反證法),所以考慮列舉勝利者
設 \(dp[winners]\) 表示勝利者為 \(winners\) 的概率,則期望為 \(\sum_{winners}dp[winners]\times|winners|\)
由勝利者都在同一個強連通分量中的性質可知,所有勝利者都可以擊敗每一個非勝利者,否則與強連通分量矛盾
那麼計算 \(dp[winners]\) 可以考慮容斥,即列舉的 \(winners\) 可以擊敗所有非勝利者的情況中,減去勝利者為 \(winners\)
時間複雜度 \(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\n", ans); return 0; }