hdu 2874 Connections between cities (LCA)

题目链接:   hdu 2874

题目大意:   在有权值森林中,任意查询两个结点的最短距离

                  若两点不联通则输出Not connected

解题思路:   用并查集把边连接的两个结点合并

                  查询的时候先判断两点是否在同一个联通图,不联通则直接输出

                  如何求联通块内任意结点间的距离呢?Floyd 一万个顶点O(n^3)肯定爆掉了

                  在一棵树中,假设某点K为根结点,数组dist[ i ]存储i点到达根结点的距离

                  因为是树,所以这个距离唯一且最短                  

                  画一画dist[ a ]和dist[ b ]的路径发现它们有重合的部分x

                  从顶点a到顶点b的距离是L,则L+2x=dist[ a ]+dist[ b ]

                  再仔细想一想发现x即是a和b最近公共祖先到根结点的距离dist[ LCA(a,b) ]

                  先确定dist[ ]的值,然后离线查找最近公共祖先既可求出最短距离,时间复杂度O(n+m)

代码:

//Final  LCA离线查找 两点最短距离
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 10100
struct {
    int to,w,next;
}Hash[MAX*3],Qes[MAX*200];  //Hash是存在的边,Qes是需要查询点

int n,Index1,Index2,parent[MAX],ansetor[MAX],pre1[MAX],pre2[MAX],answer[MAX*200];
int visit[MAX],dist[MAX];

void Add_edge(int a,int b,int w)   //建立树
{
    Hash[Index1].to=b; Hash[Index1].w=w;
    Hash[Index1].next=pre1[a];
    pre1[a]=Index1++;
}

void Add_Qes(int a,int b,int w)    //建立离线查询
{
    Qes[Index2].to=b; Qes[Index2].w=w;
    Qes[Index2].next=pre2[a];
    pre2[a]=Index2++;
}

void Init(int n)       //parent区分不同的联通块,ansetor在线更新最近祖先
{
    for(int i=1;i<=n;i++)
        parent[i]=ansetor[i]=i;
}

int Find(int x)            //区分联通块
{
    int s,j;
    s=x;
    while(x!=parent[x])
        x=parent[x];
    while(s!=x)
    {
        j=parent[s];
        parent[s]=x;
        s=j;
    }
    return x;
}

void Union(int r1,int r2)   //区分联通块
{
    int R1,R2;
    R1=Find(r1);
    R2=Find(r2);
    if(R1!=R2)
        parent[R1]=R2;
}

int Find2(int x)           //最近公共祖先查找
{
    int s,j;
    s=x;
    while(x!=ansetor[x])
        x=ansetor[x];
    while(s!=x)
    {
        j=ansetor[s];
        ansetor[s]=x;
        s=j;
    }
    return x;
}

void LCA(int u)     //离线查找最近公共祖先,并且在线更新当前点到根结点的距离
{
    int i,v;
    visit[u]=1;
    ansetor[u]=u;
    for(i=pre1[u];i!=-1;i=Hash[i].next)
    {
        v=Hash[i].to;
        if(!visit[v])        //如果此点没有访问过,则更新dist[]
        {
            dist[v]=dist[u]+Hash[i].w;
            LCA(v);          //回溯
            ansetor[v]=u;    //当前点的祖先暂时是u
        }
    }
    for(i=pre2[u];i!=-1;i=Qes[i].next)
    {
        v=Qes[i].to;
        if(visit[v])          //如果这个点之前访问过,那么它最近的祖先也就确定了
        {
            answer[Qes[i].w]=dist[u]+dist[v]-dist[ansetor[Find2(v)]]*2;
        }
    }
}

int main()
{
    int i,m,Q,a,b,c;
    while(scanf("%d%d%d",&n,&m,&Q)!=EOF)
    {
        Init(n);
        Index1=Index2=0;
        memset(dist,0,sizeof(dist));
        memset(pre1,-1,sizeof(pre1));
        memset(pre2,-1,sizeof(pre2));
        memset(visit,0,sizeof(visit));
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            Add_edge(a,b,c);    //**如果树有结构的才用fathernum[]
            Add_edge(b,a,c);    //**
            Union(a,b);
        }
        memset(answer,-1,sizeof(answer));
        for(i=1;i<=Q;i++)
        {
            scanf("%d%d",&a,&b);
            if(Find(a)==Find(b))      //存在边的点并入一个联通快
            {
                Add_Qes(a,b,i);
                Add_Qes(b,a,i);
            }
        }
        for(i=1;i<=n;i++)          
        {
            if(!visit[i])     //**没有访问过的点开始访问
                LCA(i);
        }
        for(i=1;i<=Q;i++)
        {
            if(answer[i]==-1)
                printf("Not connected\n");
            else
                printf("%d\n",answer[i]);
        }
    }
    return 0;
}


你可能感兴趣的:(HDU,LCA,2874)