• [树形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;
    }
    
  • 相关阅读:
    Ubuntu14.04LTS系统QQ的安装:pidgin-lwqq
    Ubuntu14.04LTS系统输入法的安装
    Linux系统安装及初始化(ubuntu14.04)
    创建RAID并永久挂载RAID
    磁盘管理和磁盘配额
    用户账号组账号概述
    安装及管理程序
    目录和文件管理
    Vi编辑器的工作模式
    Linux命令及使用方法
  • 原文地址:https://www.cnblogs.com/AEMShana/p/13885619.html
Copyright © 2020-2023  润新知