您的位置:首页 > 其它

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

2016-03-29 11:23 471 查看
题目: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 <bits/stdc++.h>
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<maxn;i++)
for(int j=0;j<20;j++)
anc[i][j]=1;
}

pair<int,LL> 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<LOG;j++)
anc[to][j]=anc[anc[to][j-1]][j-1];
pair<int,int> 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<LOG;i++)
if(x&(1<<i))
a=anc[a][i];
return a;
}
int Lca(int u,int v)
{
if(deep[u]<deep[v])
swap(u,v);
u=Jump(u,deep[u]-deep[v]);
if(u==v) return u;
for(int i=LOG-1;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]<deep[v])
swap(u,v);
int base=deep[u]-deep[v];
double ans=base;
int ancSon=Jump(u,deep[u]-deep[v]-1);
LL tn = sz[u];
LL tm = n-sz[ancSon];
LL gxu= gxall[lca]-(gx[ancSon]+sz[ancSon]);
LL gxv= gx[u];
ans+=double(tn*gxu+tm*gxv)/(tn*tm);
printf("%.10lf\n",ans+1);
}
}

int main()
{
Init();
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs(1,0);
dfs2(1);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
solve(u,v);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息