1. 程式人生 > 其它 >概率DP——CF1540B Tree Array

概率DP——CF1540B Tree Array

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;	
}