poj 1968 Distance Queries LCA Tarjan 离线算法

链接:http://poj.org/problem?id=1986

算法概述:所谓离线,就是把所有要求公共祖先的结点对用邻接表存起来,输入完毕后一起计算。

算法实现不困难,就是并查集和DFS。

从根开始遍历,遍历到新的结点时将该点记录为根(H[u]=u),建立以这个点为根的集合。对这个集合的每个子树进行搜索,即将子树内部的存在的所有LCA询问解决。当搜索到LCA的一个点,另外一个点被询问过了,最近公共祖先就是另外一个点此时的根。如果有子树中LCA没有解决,则其LCA询问不在这个根的子树内,将该子树所有点的根记录为u。由于是递归实现,则保证最先找到的最近的祖先。看代码显而易见。

题意:给N个农场,给出M对农场之间的距离。计算K对农场的距离。

思路:是一道标准的模板题。数据量到不能用最短路算,所以用LCA。记录从根到每个点之间的深度,答案就是ans[i]=d[query[i].v]+d[u]-2*d[findset(query[i].v)]。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<ctype.h>
#include<algorithm>
#include<string>
#define PI acos(-1.0)
#define maxn 100005
#define INF 1<<25
#define MAX 0x7fffffff
typedef long long ll;
using namespace std;
struct Edge
{
    int v,w;
    int next;
} edge[maxn],query[maxn];
int ans[maxn],point[maxn],head[maxn],d[maxn],aa[maxn],H[maxn],vv[maxn],tot,tt;;
int e_top,q_top;
int init()
{
    e_top=q_top=0;
    memset(vv,0,sizeof(vv));
    memset(head,-1,sizeof(head));
    memset(aa,0,sizeof(aa));
    memset(point,-1,sizeof(point));
    memset(ans,0,sizeof(ans));
    memset(d,0,sizeof(d));
    memset(H,0,sizeof(H));
}
int add_edge(int u,int v,int w)
{
    edge[e_top].v=v;
    edge[e_top].w=w;
    edge[e_top].next=head[u];
    head[u]=e_top++;
    edge[e_top].v=u;
    edge[e_top].w=w;
    edge[e_top].next=head[v];
    head[v]=e_top++;
}
int add_query(int u,int v)
{
    query[q_top].v=v;
    query[q_top].next=point[u];
    point[u]=q_top++;
    query[q_top].v=u;
    query[q_top].next=point[v];
    point[v]=q_top++;
}
int findset(int x)
{
    return x==H[x]?x:H[x]=findset(H[x]);
}
int dfs(int u,int w)
{
    d[u]=w,H[u]=u;
    vv[u]=1;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        if(!vv[edge[i].v])
        {
            dfs(edge[i].v,w+edge[i].w);
            H[edge[i].v]=u;
        }
    }
    for(int i=point[u];i!=-1;i=query[i].next)
    {
        if(vv[query[i].v])
        ans[i]=d[query[i].v]+d[u]-2*d[findset(query[i].v)];
    }
}
int main()
{
    scanf("%d%d",&tot,&tt);
    init();
    for(int i=0;i<tt;i++)
    {
        int x,y,z;
        char ss[2];
        scanf("%d%d%d%s",&x,&y,&z,ss);
        add_edge(x,y,z);
    }
    scanf("%d",&tt);
    for(int i=0;i<tt;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add_query(x,y);
    }
    dfs(1,0);
    for(int i=0;i<tt*2;i+=2)
    {
        if(ans[i]==0)
        printf("%d\n",ans[i+1]);
        else
        printf("%d\n",ans[i]);
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值