hdu 2874 LCA 离线算法

/*
第一次写最近公共祖先问题,用的邻接表指针。

对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。

       0

       |

       1

     /   \

   2      3

比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点。

在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。以此类推。。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共最先只有一个。对于每个集合而言可以用并查集来优化,时间复杂度就大大降低了,为O(n + q),n为总结点数,q为询问结点对数。

另外Tarjan解法,是一个离线算法,就是说它必须将所有询问先记录下来,再一次性的求出每个点对的最近公共祖先,只有这样才可以达到降低时间复杂度。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10012;//node
const int maxm=2222252;//query
int n,m,c,head[maxn],Head[maxn],dis[maxn],vis[maxn],fa[maxn],ans[maxm],id[maxn],NE,cnt;
struct node
{
    int v,d,next;
}Edge[maxm],edge[maxm];
void addEdge(int u,int v,int d)
{
    Edge[NE].v=v,Edge[NE].d=d,Edge[NE].next=head[u];
    head[u]=NE++;
}
void addedge(int u,int v,int d)
{
    edge[NE].v=v,edge[NE].d=d,edge[NE].next=Head[u];
    Head[u]=NE++;
}
int find(int i)
{
    return (fa[i]==i?i:fa[i]=find(fa[i]));
}
void Tarjan(int u){
    id[u]=cnt;
    vis[u] = 1;
    fa[u] = u;
    for (int i = Head[u];i!=-1; i=edge[i].next){
        int v=edge[i].v;
        if(vis[v]){
            if(id[u]==id[v])//同一颗子树下
            {

                 int rt=find(v); // 存的是最近公共祖先结点
                 ans[edge[i].d]=dis[u]+dis[v]-2*dis[rt];
               //  cout<<"OK "<<edge[i].d<<" "<<ans[edge[i].d]<<endl;
            }else ans[edge[i].d]=-1;
        }
    }
    for(int i= head[u];i!=-1;i=Edge[i].next){
        int v=Edge[i].v;
        if(!vis[v]){
            dis[v] = dis[u] + Edge[i].d;
            Tarjan(v);
            fa[v] = u;
        }
    }
}
int main()
{
   //freopen("//media/学习/ACM/input.txt","r",stdin);
    int i,j,u,v,d;
    while(scanf("%d%d%d",&n,&m,&c)!=EOF)
    {
        for(i=1;i<=n;i++)fa[i]=0,ans[i]=id[i]=vis[i]=0,head[i]=Head[i]=-1;
        for(NE=0,i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&d);
            addEdge(u,v,d);
            addEdge(v,u,d);
        }
        for(NE=0,i=1;i<=c;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v,i);
            addedge(v,u,i);
        }
        for(cnt=i=1;i<=n;i++,cnt++)
        {
            if(!vis[i])
            {
                dis[i]=0;
                Tarjan(i);
            }
        }
        for(i=1;i<=c;i++)
        {
            if(ans[i]>=0)
            {
                  printf("%d\n",ans[i]);
            }
            else printf("Not connected\n");
        }
    }
    return 0;
}

你可能感兴趣的:(hdu 2874 LCA 离线算法)