How far away ?(LCA Tarjan算法)

题目传送门:HDU-2586
这道题题意很简单,给你了n个村子,然后给了n-1条带权值的边,(就等于说给了你一棵树,m次询问,问你x道y的最小距离是多少。
很明显就是让求两个点的lLCA,(倍增法还是很好用的),今天准备练习一下用Tarjan算法求一下LCA,就拿这道题来练练手了。
Trajan求LCA算法是一个离线算法 O ( N M ) O(N*M) ,时间复杂度可能要比倍增法 O ( N M l o g N ) (O(N*M*logN)) 要更优那么一丢丢,我感觉其实感觉差别并不大 (只求不要打脸)
说一下这个算法的大致思路吧
1,我们DFS这整颗树,DFS遍历的过程中,我们把结点分为三类,用vis[]数组标记
vis[i]=2说明这个结点已经遍历过并且回溯过了。
vis[i]=1说明这个点遍历过但是还没有回溯。
vis[i]=1说明这个点还没有被遍历。
这样做的好处是,因为DFS遍历的特点,我们可以知道,对于正在访问的结点i,那么从根节点到当前结点i的路径都已经被标记为1,(换句话说就是i的祖先都被标记为1)如果j是已经是回溯过的点,那么LCA(i,j)就是j往上找到vis[]为1的结点,(这一步我们可以用并查集实现,把vis[j]=2的点所在集合附属到前面vis[]为1的集合中,具体可以看代码实现)。
因为是离线算法,用vector记录样例序号。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<math.h>
#include<bits/stdc++.h>
#include<map>
using namespace std;
#define LL long long
double eps=1e-10;
const LL N=1e5+10;
struct zxc
{
    int to,net,w;
}e[N*2];
int head[N];
int ans[250];
int val[N];
int vis[N];
int fa[N];
int tot=1;
int n,m;
vector<int>q[N],id[N];
void init()
{
    tot=1;
    memset(head,-1,sizeof(head));
    memset(vis ,0,sizeof(vis));
    for(int i=0;i<=n;i++)
    {
        fa[i]=i;
        q[i].clear();
        id[i].clear();
    }
}
int fin(int x)
{
    if(fa[x]!=x)
    {
        return fa[x]=fin(fa[x]);
    }
    return fa[x];
}
void add(int u,int v,int w)
{
    e[tot]={v,head[u],w};
    head[u]=tot++;
    e[tot]={u,head[v],w};
    head[v]=tot++;
}
void dfs(int x)
{
   vis[x]=1;
    for(int i=head[x];i!=-1;i=e[i].net)
    {
        int y=e[i].to;
        //printf("%d**\n",y);
        if(vis[y])continue;
        val[y]=val[x]+e[i].w;
        dfs(y);
        fa[y]=x;
    }
    for(int i=0;i<q[x].size();i++)
    {
        int idd=id[x][i];
        int z=q[x][i];
        if(vis[z]==2)
        {
            int w=fin(z);
            ans[idd]=min(ans[idd],val[x]+val[z]-2*val[w]);
        }
    }
    vis[x]=2;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            if(x==y)
            {
                ans[i]=0;
            }
            else
            {
                q[x].push_back(y);
            id[x].push_back(i);
            q[y].push_back(x);
            id[y].push_back(i);
            ans[i]=0x3f3f3f3f;
            }


        }
        dfs(1);
        for(int i=1;i<=m;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43402296/article/details/105733503