CF1540B Tree Array

CF1540B Tree Array

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


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

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

​ 考虑求 x,yxy 前面的概率。

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

fa,b=p×fa1,b+p×fa,b1+(12p)×fa,b

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

fa,b=p×fa1,b+p×fa,b1+(12p)×fa,b2p×fa,b=p×fa1,b+p×fa,b1fa,b=12×(fa1,b+fa,b1)

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

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

代码如下:

#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 @   夜空之星  阅读(37)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示