CF1540B Tree Array

CF1540B Tree Array

https://codeforces.com/contest/1540/problem/B


​ 一种朴素的想法是求出每个逆序对出现的概率然后累加得到答案。

​ 由于 \(n\) 很小,我们可以考虑枚举每个点为根把树固定住进行计算。

​ 考虑求 \(x,y\)\(x\)\(y\) 前面的概率。

​ 我们容易发现,在 \(x,y\) 的 lca 被标记之前,这个树其他部位怎么被标记都是不影响他们之间的概率的。而 \(x,y\) 的 lca 被标记后,我们就得考虑 \(x,y\) 所在的到 lca 的链被标记的情况。我们设 \(f_{a,b}\) 表示 lca 到 \(x\) 距离为 \(a\),到 \(y\) 的距离为 \(b\) 的情况下 \(x\) 的位置在 \(y\) 之前的概率。设随机选到一个点的概率是 \(p\)。那么就有式子:

\[f_{a,b}=p\times f_{a-1,b}+p\times f_{a,b-1}+(1-2p)\times f_{a,b} \]

由于 \(p\) 是会随着情况变化而变化的,所以看起来这个式子没有卵用,我们试试化简它。

\[\begin{aligned} f_{a,b}&=p\times f_{a-1,b}+p\times f_{a,b-1}+(1-2p)\times f_{a,b} \\ 2p\times f_{a,b}&=p\times f_{a-1,b}+p\times f_{a,b-1} \\ f_{a,b}&=\dfrac{1}{2}\times(f_{a-1,b}+f_{a,b-1}) \end{aligned} \]

我们发现,\(p\) 的值实际上跟 \(f\) 的取值并没有任何关系,所以我们可以直接递推出 \(f\) 的取值。边界就是 \(f_{0,i}=1,f_{0,i}=0\)

那么我们只要枚举根,再枚举两个节点,然后求出 lca ,求出之间的距离把对应的 \(f\) 值累计上就好了。总时间复杂度 \(O(n^3\times\log n)\)

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 205;
const int MOD = 1e9+7;
int n,f[MAXN][21],dep[MAXN],dp[MAXN][MAXN],ans,lg[MAXN];
vector <int> e[MAXN];
ll qpw(ll x,ll b)
{
	ll now=1,tmp=x,ans=1;
	while(now<=b)
	{
		if(now&b) ans=tmp*ans%MOD;
		tmp=tmp*tmp%MOD;
		now<<=1;
	}
	return ans;
}
void dfs(int p,int fa)
{
	dep[p]=dep[fa]+1;f[p][0]=fa;
	for(int i=1;i<=lg[dep[p]];++i) f[p][i]=f[f[p][i-1]][i-1];
	for(int i=0;i<e[p].size();++i)
	{
		int to=e[p][i];
		if(to==fa) continue;
		dfs(to,p);
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	while(dep[x]>dep[y]) x=f[x][lg[dep[x]-dep[y]]-1];
	if(x==y) return x;
	for(int i=lg[dep[x]];i>=0;--i)
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
void Solve(int x)
{
	memset(f,0,sizeof f);
	dfs(x,0);
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<i;++j)
		{
			int fa=lca(i,j);
			ans=(1ll*ans+1ll*dp[dep[i]-dep[fa]][dep[j]-dep[fa]]*qpw(n,MOD-2)%MOD)%MOD;
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for(int i=1;i<n;++i)
	{
		int u,v;
		scanf("%d %d",&u,&v);
		e[u].push_back(v);e[v].push_back(u);
	}
	for(int i=1;i<=n;++i) dp[0][i]=1,dp[i][0]=0;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			dp[i][j]=1ll*(dp[i-1][j]+dp[i][j-1])%MOD*qpw(2,MOD-2)%MOD;
	for(int i=1;i<=n;++i) Solve(i);
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-12-31 20:56  夜空之星  阅读(35)  评论(0编辑  收藏  举报