概率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;	
} 
posted @ 2021-07-15 13:07  斌斌翻水水  阅读(38)  评论(0)    收藏  举报