hdu 2874 Connections between cities 最近公共祖先lca(离线算法/tarjan算法)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e4+10;
const int maxc=1e6+10;
struct node{
    int to,w;
    node(int a=0,int b=0){to=a;w=b;}
};
int f[maxn],dis[maxn],n,ans[maxc],vis[maxn],mark[maxn];
//f[i]并查集所用,记录前继结点
//dis[i]记录个点到跟结点的距离
//ans记录m个询问的答案
//vis标记查询过了的点
//mark记录已访问过的根节点。由于已遍历的树所有结点的find都是根节点,这样就能判断下是否在别的树里了
vector<node>e[maxn];//记录树
vector<node>q[maxn];//记录所求最短距离的两点
int find(int x)
{
    if(x!=f[x])f[x]=find(f[x]);
    return f[x];
}
void lca(int u)
{
    int i,j,k,v,c;
    for(j=0;j<q[u].size();j++)
    {
        c=q[u][j].to;
        //如果所求两点中的对应点已知,则必定在同一子树上,由于并查集只记录所在子树。所以find(c),就是最近公共祖先
        if(vis[c]&&ans[q[u][j].w]==-1&&mark[find(c)]!=1)
        {
            //cout<<f[c]<<"***"<<endl;
            ans[q[u][j].w]=dis[u]+dis[c]-2*dis[find(c)];
        }
    }
    for(i=0;i<e[u].size();i++)
    {
        v=e[u][i].to;
        if(vis[v])continue;
        vis[v]=1;
        dis[v]=dis[u]+e[u][i].w;
        lca(v);//深度优先搜索
        f[v]=u;

    }
}
int main()
{
    int n,m,c;
    while(scanf("%d%d%d",&n,&m,&c)!=EOF)
    {
        int i,j,k,x,y,z;
        for(i=1;i<=n;i++)
        {
            e[i].clear();
            q[i].clear();
            f[i]=i;
            vis[i]=mark[i]=0;
        }
        memset(ans,-1,sizeof(ans));
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            e[x].push_back(node(y,z));
            e[y].push_back(node(x,z));
        }
        for(i=1;i<=c;i++)
        {
            scanf("%d%d",&x,&y);
           // if(x==y){ans[i]=0;continue;}
            q[x].push_back(node(y,i));
            q[y].push_back(node(x,i));
        }
        for(i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                vis[i]=1;
                dis[i]=0;
                lca(i);
                mark[i]=1;
            }
        }
        for(i=1;i<=c;i++)
        {
            if(ans[i]!=-1)
                printf("%d\n",ans[i]);
            else
                printf("Not connected\n");
        }
    }
    return 0;
}
/*
    最近公共祖先lca 离线算法/Tarjan算法,用mark标记是否在同一组里。 
     
    方法举例说明: 
                1 
               / \ 
              2   3 
             / \ 
            4   5 
           /   / 
          7   8 
         /  
        9   
    查询(4,9):到4时,由于vis[9]=0,所以继续;到9后,最近公共祖先就是f[4]=4(回溯的时候记录父节点); 
    查询(9,8):深度优先搜索,所以到8时才询问;要到8必须回溯到2,在进到8这个子树,所以以记录f[9]=7;f[7]=4;f[4]=2;f[2]=2;所以find(2)=2; 
    查询(8,3):跟上条相似,必须回溯1才能到3,而find(8)=1就是最近公共祖先; 
    我们可以发现,对于查询的两点,都要在先查询到的点开始,回溯到最近公共祖先,才查询相对应的点。这也就是离线算法的思路了。。这是我自己理解的。。
    
    专业解释请百度。。
*/

你可能感兴趣的:(lca离线算法)