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

浙公网安备 33010602011771号