codeforces #343 E. Famil Door and Roads (最近公共祖先LCA+一点点概率)

题目:http://codeforces.com/contest/629/problem/E

题意:给定一棵n(n<100000)个节点的树,有m(m<100000)次查询,对于每次查询,给定两个顶点u和v,然后你要再添加一条边,使树上的两个顶点连接(构成一个环),并且u和v都在环内。求这个环的期望长度。

分析:

这题认真分析的话,其实很简单。

首先定义gx[cur]表示以cur为祖先其后代节点到cur的路径的长度之和。

定义gxall[cur]表示树上所有点到cur的路径的长度之和。

sz[cur]表示以cur为根的子树的大小。

分两种情况讨论:

①当u和v不是另外一个点的祖先。

假如以u为根的子树上的顶点集合为{a1,a2.....ax}

以v为根的子树上的顶点集合为{b1,b2.....by}

那么对于其中的一种组合的可能长度为dis(ai,u)+dis(bi,v)+dis(u,v)+1,其对答案的贡献为(dis(ai,u)+dis(bi,v)+dis(u,v)+1)/(x*y)。

那么期望长度为(y*gx[u]+x*gx[v])/(x*y)+dis(u,v)+1。

②当u是v的祖先或者v是u的祖先

同样的方法。只是涉及上面的点到当前点的长度和。根据gx[]推出gxall[]就行了。

代码:

#include 
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;

const int maxn = 200007;
const int LOG = 20;

struct node
{
	int v,next;	
}List[maxn];
int head[maxn],cnt;
int n,m;
void add(int u,int v)
{
	List[cnt].v=v;
	List[cnt].next=head[u];
	head[u]=cnt++;
}

int sz[maxn],anc[maxn][LOG],deep[maxn];
LL gx[maxn],gxall[maxn];

void Init()
{
	memset(head,-1,sizeof(head));
	cnt=0;
	for(int i=0;i dfs(int cur,int dp)  //返回节点数和贡献 
{
	deep[cur]=dp;
	sz[cur]=1;
	for(int i=head[cur];~i;i=List[i].next)
	{
		int to = List[i].v;
		if(anc[cur][0]!=to)
		{
			anc[to][0]=cur;
			for(int j=1;j pii = dfs(to,dp+1);
			sz[cur]+=pii.first;
			gx[cur]+=pii.first+gx[to];
		}
	}
	return make_pair(sz[cur],gx[cur]);
}
void dfs2(int cur)
{
	int fa=anc[cur][0];
	//gxall[cur]=gxall[fa]-(gx[cur]+sz[cur])+(n-sz[cur])+gx[cur];
	if(cur!=1) 
		gxall[cur]=gxall[fa]+n-2*sz[cur]; //计算所有点到当前点的贡献 
	else
		gxall[cur]=gx[cur];
	for(int i=head[cur];~i;i=List[i].next)
		if(anc[cur][0]!=List[i].v)
			dfs2(List[i].v);
}
int Jump(int a,int x)
{
	for(int i=0;i=0;i--)
	{
		if(anc[u][i]!=anc[v][i])
		{
			u=anc[u][i];
			v=anc[v][i];
		}
	}
	return anc[u][0];
}

void solve(int u,int v)
{
	int lca=Lca(u,v);
	if(lca!=u && lca!=v)
	{
		int base=deep[u]-deep[lca]+(deep[v]-deep[lca]);
		LL tn=sz[v];
		LL tm=sz[u];
		double ans=base;
		ans+=double(tn*gx[u]+tm*gx[v])/(tn*tm);
		printf("%.10lf\n",ans+1);
	}
	else
	{
		if(deep[u]

你可能感兴趣的:(ACM-数据结构,ACM-数学)