GDOI【JZOJ4794】富爷说是一棵树

来源:互联网 发布:vb自动更新登陆器源码 编辑:程序博客网 时间:2024/05/20 03:39

Description

富爷说来一棵树,于是大头栽了一棵树。树大了,有n个点和n - 1条边,任意两个点都是联通的,点的标号为1 - n。爱树的大头和富爷在树上安居乐业,但大头住在u,而富爷住在v,他们都很不高兴,因为u到v有且只有一条简单路径。
当然了,树王富爷找到了解决办法,他打算带着大头再给树建一条边(保证不是自环),而且他们会在n * (n - 1) / 2的方案中随机选择一种。
但,要让富爷和大头开心是有条件的。只有新建边之后,富爷去大头家以及大头去富爷家存在两条路径不会走相同的边时,他们才会呵呵(也就是说 存在一个简单环包含u和v)。
不开心的事情选择忘记。当富爷和大头开心时,你能得到愉快值等于环的大小。所以,你要告诉富爷和大头,当他们开心时(只考虑在环内),他们的期望愉悦值。

Data Constraint

20% n,m <= 2000
40% n,m <= 10^5 树是随机的
60% n,m <= 10^5 每个点的度数均为2
100% n,m <= 10^5

Solution

这种题一看就知道是用lca搞最近公共祖先。我们对情况分一下类:

1、当询问x,y不是祖先关系时(即x不是y的儿子或y不是x的儿子),我们只需将双方的子树的大小分别乘上对方子树所有点到该子树顶点的距离,相加再除以情况数即可。式子为ans=(size[x]*g[y]+size[y] *g[x])/(size[x] *size[y])(g[i]表示以i为根的子树所有点到该子树顶点i的距离)

2、当询问x,y是祖先关系时,这时我们就要考虑,设k是y的直接儿子且k是x的祖先。这时的连边情况的两个集合显然是(1)除k的子树以外,整棵树所有点和(2)x的子树相连(为什么,你画画图就知道啦)。所以我们仿照情况1已进行计算,稍加改变即可。

代码

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<algorithm>#define ll long longusing namespace std;const int maxn=200005;int first[maxn],last[maxn],next[maxn],f[maxn][20];ll n,m,i,t,j,k,l,x,y,num,v[maxn],fa[maxn];ll size[maxn],g[maxn],p[maxn],deep[maxn];bool bz[maxn];double len,ans;void lian(int x,int y){    last[++num]=y;next[num]=first[x];first[x]=num;}int lca(int x,int y){    int i,j,t=0,k,l;    if (deep[x]<deep[y]) swap(x,y);    for (i=log(n)/log(2);i>=0;i--)        if (deep[f[x][i]]>=deep[y]) len+=(1<<i),x=f[x][i];    if (x==y) return x;    for (i=log(n)/log(2);i>=0;i--)        if (f[x][i]!=f[y][i]) len+=2*(1<<i),x=f[x][i],y=f[y][i];    len+=2;    return f[x][0];}int main(){//  freopen("data.in","r",stdin);freopen("data.uot","w",stdout);    scanf("%d%d",&n,&m);    for (i=1;i<n;i++)        scanf("%d%d",&x,&y),lian(x,y),lian(y,x);    for (i=1;i<=n;i++)        size[i]=1;    v[1]=1;i=0;j=1;deep[1]=1;bz[1]=true;        while (i<j){        x=v[++i];        for (t=first[x];t;t=next[t]){            if (last[t]==fa[x])continue;            bz[last[t]]=true;            v[++j]=last[t];fa[last[t]]=x;deep[v[j]]=deep[x]+1;        }    }    while (j) size[fa[v[j]]]+=size[v[j]],f[v[j]][0]=fa[v[j]],g[fa[v[j]]]+=g[v[j]]+size[v[j]],j--;    for (i=2;i<=n;i++){        p[v[i]]=g[fa[v[i]]]-g[v[i]]+p[fa[v[i]]]+n-2*size[v[i]];    }    for (j=1;j<=log(n)/log(2);j++)        for (i=1;i<=n;i++)            f[i][j]=f[f[i][j-1]][j-1];    for (i=1;i<=m;i++){        scanf("%d%d",&x,&y);len=0;        if (deep[x]<deep[y]) swap(x,y);        t=lca(x,y);        if (t!=y) ans=len+(g[x]*size[y]+g[y]*size[x])*1.0/(size[x]*size[y])+1;        else{            k=x;            for (j=log(n)/log(2);j>=0;j--)                if (deep[f[k][j]]>deep[y]) k=f[k][j];            t=g[y]+p[y]-g[k]-size[k];            l=n-size[k];            ans=g[x]*l+t*size[x]+(len+1)*(size[x]*l);            ans=ans/(size[x]*l*1.0);        }           printf("%.8lf\n",ans);    }}
3 0
原创粉丝点击