[树形dp] Codeforces 629E Famil Door and Roads

题目大意

给出一棵 (n) 个节点的树。有 (m) 个询问,每一个询问包含两个数 (a,b)

我们可以对任意两个不相连的点连一条无向边,并且使得加上这条边后 (a,b) 处在一个环内。

对于每一个询问,求这样的环的期望长度。
(2leq n,mleq 10^5)

题解

树形dp+二次扫描换根。
设询问 (u,v) 这两个点,(usim v) 的这条树链把树分为三部分:

  1. (u) 出发,不经过 (usim v) 这条树链,所能到达的点集,记为 (A)
  2. (v) 出发,不经过 (usim v) 这条树链,所能到达的点集,记为 (B)
  3. 剩下的点构成的点集,记为 (C)

(Sum) 表示新增一条跨越 (usim v) 这条链的边构成的所有环的长度之和,计 (Num) 表示新增一条跨越 (usim v) 这条链的边的方案数。则 (Ans=frac{Sum}{Num})

[Sum=sum_{ain A}sum_{bin B}(dis(a,u)+dis(v,b)+dis(u,v)+1)\ =(dis(u,v)+1) imes|A||B|+sum_{ain A}sum_{bin B}(dis(a,u)+dis(v,b))\ =(dis(u,v)+1) imes|A||B|+sum_{ain A}sum_{bin B}dis(a,u)+sum_{ain A}sum_{bin B}dis(v,b))\ =(dis(u,v)+1) imes|A||B|+|B| imessum_{ain A} dis(a,u)+|A| imessum_{bin B} dis(v,b)\ ]

[Num=|A| imes|B| ]

不妨以(1)为根,令 (Deep[u]<Deep[v]),令

[dp[u][0]=sum_{vin subtree(u)} dis(u,v)\ dp[u][1]=sum_{v otin subtree(u)} dis(u,v) ]

(dp[u][0]) 表示从 (u) 出发向下走的所有路径的长度和,(dp[u][1]) 表示从 (u) 出发向上走的所有路径的长度和,设 (fa)(u) 的父亲,那么有

[dp[u][0]=sum_{vin son(u)}dp[v][0]+Size[v] \ dp[u][1]=dp[fa][1]+dp[fa][0]-dp[u][0]-Size[u]+N-Size[u] ]

我们可以先通过一次DFS求得 (dp[u][0]),再通过一次DFS扫描换根求得 (dp[u][1]),然后就容易求得 (Sum)
对于 (lca(u,v)=u)(lca(u,v) eq u),稍有不同,需要分类讨论。

时间复杂度 (O(nlog n))

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

struct Graph{
    struct edge{int Next,to;};
    edge G[200010];
    int head[100010];
    int cnt;

    Graph():cnt(2){}
    void clear(int node_num=0){
        cnt=2;
        if(node_num==0) memset(head,0,sizeof(head));
        else fill(head,head+node_num+5,0);
    }
    void add_edge(int u,int v){
        G[cnt].to=v;
        G[cnt].Next=head[u];
        head[u]=cnt++;
    }
};

Graph G;
int Size[100010],Deep[100010],Anc[100010][18];
LL dp[100010][2];
int N,M;

void DFS(int u,int fa){
    Size[u]=1;
    Anc[u][0]=fa;
    for(int i=1;i<18;++i)
        Anc[u][i]=Anc[Anc[u][i-1]][i-1];
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(v==fa) continue;
        Deep[v]=Deep[u]+1;
        DFS(v,u);
        Size[u]+=Size[v];
        dp[u][0]+=dp[v][0]+Size[v];
    }
    return;
}

void DFSB(int u,int fa){
    if(fa) dp[u][1]=dp[fa][1]+dp[fa][0]-dp[u][0]-Size[u]+N-Size[u];
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(v==fa) continue;
        DFSB(v,u);
    }
    return;
}

int LCA(int u,int v){
    int Root=1;
    if(u==Root || v==Root) return Root;
    if(Deep[u]<Deep[v]) swap(u,v);
    for(RG i=17;i>=0;--i){
        if(Deep[Anc[u][i]]>=Deep[v])
            u=Anc[u][i];
    }
    if(u==v) return u;
    for(RG i=17;i>=0;--i){
        if(Anc[u][i]!=Anc[v][i]){
            u=Anc[u][i];
            v=Anc[v][i];
        }
    }
    return Anc[u][0];
}

int main(){
    Read(N);Read(M);
    for(RG i=1;i<=N-1;++i){
        int u,v;
        Read(u);Read(v);
        G.add_edge(u,v);
        G.add_edge(v,u);
    }
    Deep[1]=1;
    DFS(1,0);
    DFSB(1,0);
    for(RG i=1;i<=M;++i){
        int u,v;
        Read(u);Read(v);
        if(Deep[u]>Deep[v]) swap(u,v);
        int c=LCA(u,v);
        if(c==u){
            int s=v;
            for(RG j=17;j>=0;--j)
                if(Deep[Anc[s][j]]>=Deep[u]+1)
                    s=Anc[s][j];
            LL temp=dp[u][1]+dp[u][0]-dp[s][0]-Size[s];
            LL sum=(1LL+Deep[v]-Deep[u])*(LL)Size[v]*(LL)(N-Size[s])+(LL)Size[v]*temp+(LL)(N-Size[s])*dp[v][0];
            double Ans=(double)sum/((double)Size[v]*(double)(N-Size[s]));
            printf("%.8lf
",Ans);
        }else{
            LL sum=(1LL+Deep[v]+Deep[u]-2*Deep[c])*(LL)Size[v]*(LL)Size[u]+
                (LL)Size[v]*dp[u][0]+(LL)Size[u]*dp[v][0];
            double Ans=(double)sum/((double)Size[v]*(double)Size[u]);
            printf("%.8lf
",Ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/13885619.html