Codeforces - Famil Door and Roads

题目链接:Codeforces - Famil Door and Roads


我们根据 x = lca || y = lca ,或者其他情况来讨论。

如果是前者,那么满足的环肯定是 lca 之上的一个点,和当前深度大的节点的子节点。

如果是后者,那么肯定是两个节点的子节点。

所以我们对每一部分单独求期望即可。某个节点之下的节点到当前节点的期望,我们可以用总距离除以点数计算。所以计算这个先dp,然后换根即可。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
#define int long long
using namespace std;
const int N=1e5+10;
int n,m,f[N][20],dp[N],sum[N],sz[N],dep[N];
vector<int> g[N];
void dfs1(int x,int fa){
	sz[x]=1; dep[x]=dep[fa]+1; f[x][0]=fa;
	for(int i=1;i<=17;i++)	f[x][i]=f[f[x][i-1]][i-1];
	for(int to:g[x]) if(to!=fa) dfs1(to,x),dp[x]+=dp[to]+sz[to],sz[x]+=sz[to]; 
}
void dfs2(int x,int fa){
	for(int to:g[x]) if(to!=fa){
		sum[to]=dp[to]+(sum[x]-sz[to]-dp[to])+(n-sz[to]);
		dfs2(to,x);
	}
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])	swap(x,y);
	for(int i=17;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y)	return x;
	for(int i=17;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
inline int get(int x,int y){
	for(int i=17;i>=0;i--)	if(dep[f[x][i]]>dep[y])	x=f[x][i];
	return x;
}
inline void calc(int x,int y){
	int lc=lca(x,y); double res=1.0;
	int dis=dep[x]+dep[y]-2*dep[lc];
	if(lc==x||lc==y){
		if(dep[x]>dep[y])	swap(x,y);	
		int now=get(y,x);
		res+=1.0*dp[y]/sz[y];
		res+=1.0*(sum[x]-sz[now]-dp[now])/(n-sz[now]);
	}else	res+=1.0*dp[x]/sz[x]+1.0*dp[y]/sz[y];
	printf("%.10lf\n",res+1.0*dis);
}
signed main(){
	cin>>n>>m;
	for(int i=1,a,b;i<n;i++) scanf("%lld %lld",&a,&b),g[a].push_back(b),g[b].push_back(a);
	dfs1(1,0); sum[1]=dp[1]; dfs2(1,0);
	for(int i=1,x,y;i<=m;i++)	scanf("%lld %lld",&x,&y),calc(x,y);
	return 0;
}

你可能感兴趣的:(Codeforces,树形dp,LCA)