1. 程式人生 > >Codeforces-629E Famil Door and Roads(期望)

Codeforces-629E Famil Door and Roads(期望)

題意

給定一棵 n 個節點的樹, m 個詢問,每一個詢問包含兩個引數

a , b ,我們能夠通過加一條邊使 a , b 處於同一個環內。對於每一個詢問,求這樣的環的期望長度。
1 n , m 10 5

思路

細節比較多的一道題。首先我們可以用期望的線性性質,把環的期望拆成多個值,假設邊連線

x , y ,形成 x a b y x 這樣的環。那麼 E [ ] = E [ ( x , a ) ] + E [ ( y , b ) ] + ( a , b ) + 1
不難發現,需要分類討論,分 x , y 有無祖宗關係。
假如沒有祖宗關係,那 x , y 只能分別在 a , b 的子樹中出現,我們可以維護一個 s u m 1 陣列, s u m 1 u 表示 u 的子節點到 u 的距離總和。 E [ ( x , a ) ] = s u m 1 a s z a , E [ ( y , b ) ] = s u m 1 b s z b
而若有祖宗關係,假設 a 節點更深,那麼仍有 E [ ( x , a ) ] = s u m 1 a s z a ,而 y 則可以選擇除 j m p ( a , d e p a d e p b 1 ) (設它為 z ) 這棵子樹外的所有節點,那我們方便起見,可以先預處理一個節點到其它所有節點的距離之和 s u m 2 E [ ( y , b ) ] 只用將 s u m 2 b 減去 ( s u m 1 z + s z z ) , 即 b 到子樹 z 中節點的距離總和,再除以 n s z z 即可。

程式碼

#include<bits/stdc++.h>
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 100003
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(register int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
int dfn[N],dep[N],fa[N],sz[N],son[N],top[N],ori[N],ord;
LL sum1[N],sum2[N];
int n,m;
LL gcd(LL x,LL y){return y?gcd(y,x%y):x;}

void dfs(int u,int f,int d)
{
    dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0,sum1[u]=0;
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f)continue;
        dfs(v,u,d+1);
        sum1[u]+=sum1[v]+sz[v];
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void make_path(int u,int f,int tp)
{
    dfn[u]=++ord,ori[ord]=u,top[u]=tp;
    if(son[u])make_path(son[u],u,tp);
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f||v==son[u])continue;
        make_path(v,u,v);
    }
}
int jmp(int x,int d)
{
    while(dep[x]-dep[fa[top[x]]]<=d)
    {
        d-=dep[x]-dep[fa[top[x]]];
        x=fa[top[x]];
    }
    return ori[dfn[x]-d];
}
int LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
        else y=fa[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}
void redfs(int u,int f,LL cur)
{
    sum2[u]=cur;
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f)continue;
        redfs(v,u,cur-sz[v]+(n-sz[v]));
    }
}

int main()
{
    G.clear();
    scanf("%d%d",&n,&m);
    FOR(i,1,n-1)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        G.add(u,v),G.add(v,u);
    }
    dfs(1,0,1);
    ord=0;make_path(1,0,1);
    redfs(1,0,sum1[1]);
    while(m--)
    {
        int x,y,z,lca;
        LL p1,p2,q1,q2,p,q;
        scanf("%d%d",&x,&y);
        if(dep[x]<dep[y])swap(x,y);
        lca=LCA(x,y);
        p1=sum1[x],q1=sz[x];
        if(lca==y)
        {
            z=jmp(x,dep[x]-dep[y]-1);
            p2=sum2[y]-sum1[z]-sz[z],q2=n-sz[z];
        }
        else p2=sum1[y],q2=sz[y];
        p=q1*q2*(dep[x]+dep[y]-2*dep[lca]+1)+p1*q2+p2*q1;
        q=q1*q2;
        printf("%lld/%lld\n",p/gcd(p,q),q/gcd(p,q));
    }
    return 0;
}