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

题目大意

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

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

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

题解

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

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

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

\[Sum=\sum_{a\in A}\sum_{b\in B}(dis(a,u)+dis(v,b)+dis(u,v)+1)\\ =(dis(u,v)+1)\times|A||B|+\sum_{a\in A}\sum_{b\in B}(dis(a,u)+dis(v,b))\\ =(dis(u,v)+1)\times|A||B|+\sum_{a\in A}\sum_{b\in B}dis(a,u)+\sum_{a\in A}\sum_{b\in B}dis(v,b))\\ =(dis(u,v)+1)\times|A||B|+|B|\times\sum_{a\in A} dis(a,u)+|A|\times\sum_{b\in B} dis(v,b)\\ \]

\[Num=|A|\times|B| \]

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

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

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

\[dp[u][0]=\sum_{v\in 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)\neq u\),稍有不同,需要分类讨论。

时间复杂度 \(O(n\log 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\n",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\n",Ans);
        }
    }
    return 0;
}
posted @ 2020-10-27 16:31  AE酱  阅读(78)  评论(0编辑  收藏  举报