概率DP——CF1540B Tree Array
阿新 • • 發佈:2021-07-15
CF1540B Tree Array
題目傳送門:1540B - Tree Array
這個題目是一個概率\(dp\),我們需要在每個點作為根下,算出每一個逆序對的貢獻。然後這麼算這個逆序對的出現概率呢?
我們設a和b,a和b是一個逆序對,然後x是\(lca(a, b)\)。為什麼要考慮\(lca\)呢,主要是<a,b>這個逆序對出現的概率主要是和a和b到x的那條鏈有關係。
在x之前的點不會影響概率,同時和x下面不在a,b那條鏈子的點無關。這個是為什麼呢?
因為這個點的選取是要有邊相連才能選的,而在x下面但是不在a,b鏈子上的點,即使選了並不能促進a,b這個兩個點選取的進度。
比如1-2, 2-3, 3-4, 1-5, 5-6, 2-7
我們在以1為根的時候,算<6,4>的貢獻的時候,即使中間選了7也是不會影響概率的。
那麼就好了這樣我們就可以把x點左右兩邊的鏈子上的點變成分石子問題:
\(dp[i][j]\)的意思是有兩堆石子分別是i個和j個,先取完i個石子那一堆的石子的概率:
- 初始化:\(dp[0][i] = 1 |0 <= i <= n\)第一堆沒石子先取完的概率肯定是1
- \(dp[i][j] = (dp[i-1][j]+ dp[i][j-1])/2\)
然後這裡就是把鏈的長度分成石子的個數,可以用樹上倍增來算\(lca\)用,deep陣列(點的深度)來算鏈子的長度,這裡分類討論:
- 如果a為x,那麼概率肯定是1
- 如果b為x,那麼概率肯定是0
- x既不是a也不是b,那麼貢獻就是\(dp[deep[j]-deep[x]][deep[z]-deep[x]]\)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 205; const ll mod = 1e9+7; vector<int> mp[maxn]; int n, deep[maxn], fa[maxn][25]; ll qpow(ll a, ll b){ ll sum = 1; while(b){ if(b & 1) sum = sum * a % mod; a = a * a % mod; b >>= 1; } return sum; } void dfs(int u, int f){ deep[u] = deep[f] + 1; fa[u][0] = f; for(int i = 0; i < mp[u].size(); i++){ int v = mp[u][i]; if(v == f) continue; dfs(v, u); } } void init(){ int k = log(n*1.0)/log(2.0); for(int i = 1; i <= k; i++){ for(int j = 1; j <= n; j++){ fa[j][i] = fa[fa[j][i-1]][i-1]; } } } int LCA(int u, int v){ int i; if(deep[u] < deep[v]) swap(u, v); for(i = 0; (1 << i) <= deep[u]; i++); i--; for(int j = i; j >= 0; j--){ if(deep[u] - (1 << j) >= deep[v]) u = fa[u][j]; } if(u == v) return u; for(int j = i; j >= 0; j--){ if(fa[u][j] != fa[v][j]){ u = fa[u][j]; v = fa[v][j]; } } return fa[u][0]; } int dp[maxn][maxn]; int main() { scanf("%d", &n); int u, v; for(int i = 1; i < n; i++) { scanf("%d %d", &u, &v); mp[u].push_back(v); mp[v].push_back(u); } for(int i = 1; i <= n; i++) dp[0][i] = 1; for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % mod * 500000004ll % mod; } } int ans = 0; for(int i = 1; i <= n; i++) { dfs(i, 0); init(); for(int j = 1; j <= n; j++) { for(int z = 1; z < j; z++) { int x = LCA(j, z); if(x == j) ans = (ans + 1)%mod; else if(x == z) continue; else { ans = (ans + dp[deep[j]-deep[x]][deep[z]-deep[x]])%mod; } } } } printf("%d", 1ll*ans*qpow(n, mod-2)%mod); return 0; }