zoukankan      html  css  js  c++  java
  • POJ 1986 Distance Queries (离线LCA)

    题意:给一棵带权重的树,共有k个查询,每次查询树中2个结点的距离。结点数n最大为40000,k最大10000

    分析:首先我们将无根树转为有根树,可以在O(n)时间内得到每个结点到根结点的距离。由于在树中从一个结点走到另一个结点的路径是唯一的,所以a到b的路径一定经过lca(a,b),设lca(a,b)=c。此时不难发现d(a,b)=d(a,root)+d(b,root)-2*d(c,root)。先在问题就是如何快速求LCA,由于结点数目比较大,查询比较多,所以用在线算法会超时。这里用的是tarjan离线算法,时间复杂度为O(n+k)。

    View Code
    #include <stdio.h>
    #include <string.h>
    #define N 40010
    #define M 80010
    #define Q 20010
    int n,m,q,e,eq;
    int first[N],next[M],v[M],w[M];
    int first_q[N],next_q[Q],u_q[Q],v_q[Q],lca[Q];
    int d[N],p[N];
    bool ok[Q];
    void init()
    {
        e=eq=0;
        memset(first,-1,sizeof(first));
        memset(first_q,-1,sizeof(first_q));
        memset(lca,0,sizeof(lca));
        memset(p,-1,sizeof(p));
        memset(ok,0,sizeof(ok));
    }
    void add(int a,int b,int c)
    {
        v[e]=b;
        w[e]=c;
        next[e]=first[a];
        first[a]=e++;
    }
    void add_q(int a,int b)
    {
        u_q[eq]=a;
        v_q[eq]=b;
        next_q[eq]=first_q[a];
        first_q[a]=eq++;
    }
    
    void make_set(int i)
    {
        p[i]=i;
    }
    int find_set(int i)
    {
        if(i^p[i])  p[i]=find_set(p[i]);
        return p[i];
    }
    void union_set(int i,int j)
    {
        i=find_set(i),j=find_set(j);
        p[j]=i;
    }
    void get_d(int a,int fa)
    {
        int i,b;
        for(i=first[a];i!=-1;i=next[i])
        {
            b=v[i];
            if(b^fa)    d[b]=d[a]+w[i],get_d(b,a);
        }
    }
    void dfs(int a)
    {
        int i,b;
        make_set(a);
        for(i=first[a];i!=-1;i=next[i])
        {
            b=v[i];
            if(p[b]==-1)
            {
                dfs(b);
                union_set(a,b);
            }
        }
        for(i=first_q[a];i!=-1;i=next_q[i]) if(!ok[i])
        {
            b=v_q[i];
            if(p[b]!=-1)    lca[i]=find_set(b),ok[i]=true;
        }
    }
    int main()
    {
        int a,b,c;
        char s[3];
        while(~scanf("%d%d",&n,&m))
        {
            init();
            while(m--)
            {
                scanf("%d%d%d%s",&a,&b,&c,s);
                add(a,b,c);
                add(b,a,c);
            }
            scanf("%d",&q);
            while(q--)
            {
                scanf("%d%d",&a,&b);
                add_q(a,b);
                add_q(b,a);
            }
            d[1]=0;
            get_d(1,0);
            dfs(1);
            for(int i=0;i<eq;i+=2)
            {
                a=u_q[i];
                b=v_q[i];
                c=lca[i];
                if(!c)  c=lca[i+1];
                printf("%d\n",d[a]+d[b]-2*d[c]);
            }
        }
        return 0;
    }
  • 相关阅读:
    Java在ACM中的应用
    acm->stl
    残缺棋盘--状压DP
    EOJ Monthly 2019.3 A
    【CF1141E】Superhero Battle
    AtCoder Grant Contest 10.F 博弈
    莫比乌斯反演总结
    P2257 YY的GCD
    BZOJ1011 莫比乌斯反演(基础题
    HDU1695 莫比乌斯反演
  • 原文地址:https://www.cnblogs.com/algorithms/p/2622399.html
Copyright © 2011-2022 走看看