HDU2586 How far away ?(最近公共祖先lca,离线Tarjan,最短路)

题目:

How far away ?

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 17199    Accepted Submission(s): 6622


Problem Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.
 

Input
First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
 

Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
 

Sample Input
  
  
2 3 2 1 2 10 3 1 15 1 2 2 3 2 2 1 2 100 1 2 2 1
 

Sample Output
  
  
10 25 100 100
 

Source
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   3486  2874  2888  3234  2818 
 

Statistic |  Submit |  Discuss |  Note

思路:

有n个点,n-1条边,给出了m组询问,求的是每一组询问的两个点的距离是多少。

我们可以采用离线Tarjan算法。

用dis[i]表示从根节点到当前节点的距离,然后求每组询问 的最近公共祖先,比如询问i,j两点,那么计算出:

dis[i]+dis[j]-2*dis[他们的公共祖先],就是要求的距离了


代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <string>
#include <set>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define mod 1000007
#define ll long long
using namespace std;
const int N=4e4+210;
int n,m;
vector<int>v[N],query[N],num[N],w[N];
int pre[N],dis[N],ans[N],vis[N];
void init()
{
	for(int i=1; i<=n; i++)
	{
		v[i].clear();//记录一个节点的儿子
		query[i].clear();//记录询问
		num[i].clear();//记录询问的顺序
		w[i].clear();//记录每两个点之间的权值
		dis[i]=0;//代表从根节点到当前节点的距离
		vis[i]=0;//标记一个节点有没有被访问过
		pre[i]=i;//并查集的指向
	}
}
int find(int x)
{
	if(x==pre[x])
		return x;
	else
	{
		pre[x]=find(pre[x]);
		return pre[x];
	}
}
void mix(int x,int y)
{
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy)
		pre[fy]=fx;
}
void Tarjan(int rt,int val)
{
	vis[rt]=1;
	dis[rt]=val;
	for(int i=0; i<v[rt].size(); i++)
	{
		int tmp=v[rt][i];//找出其中一个儿子节点
		if(!vis[tmp])
		{
			Tarjan(tmp,val+w[rt][i]);//进入下一层继续找
			mix(rt,tmp);//把当前节点和它的父亲节点加入并查集
		}
	}
	for(int i=0; i<query[rt].size(); i++)
	{
		int tmp=query[rt][i];//找到当前询问的点
		if(vis[tmp])//如果当前的点被标记过,那么他们的最近公共祖先就是find(tmp),然后计算出路径
			ans[num[rt][i]]=dis[rt]+dis[tmp]-2*dis[find(tmp)];
	}
}
int main()
{
	int t,a,b,c;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		init();
		for(int i=1; i<n; i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			v[a].push_back(b);
			w[a].push_back(c);
			v[b].push_back(a);
			w[b].push_back(c);
		}
		for(int i=1; i<=m; i++)
		{
			scanf("%d%d",&a,&b);
			query[a].push_back(b);
			num[a].push_back(i);
			query[b].push_back(a);
			num[b].push_back(i);
		}
		Tarjan(1,0);
		for(int i=1; i<=m; i++)
			printf("%d\n",ans[i]);
	}
	return 0;
}

2018年01月23日更新二刷后的代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define inf 1000000
#define mem(a,b) memset(a,b,sizeof(a))
const int N=100000+7;
const int M=2*N;
int pre[N],first[N],first2[N],tot,tot2;
bool vis[N];//标记有没有询问
int n;
int fa[N],ans[N],cnt;
int vis2[N],dis[N];
map<string,int>mp;
struct edge
{
    int v,next,w;
} e[M];
struct Query
{
    int v,next,id;
} query[M];

void add_edge(int u,int v,int w)
{
    e[tot].v=v;
    e[tot].w=w;
    e[tot].next=first[u];
    first[u]=tot++;
}

void add_query(int u,int v,int id)
{
    query[tot2].id=id;
    query[tot2].v=v;
    query[tot2].next=first2[u];
    first2[u]=tot2++;
}

int find(int x)
{
    return x==pre[x]?x:pre[x]=find(pre[x]);
}

void lca(int u,int fa)
{
    for(int i=first[u]; ~i; i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa) continue;
        lca(v,u);
        pre[v]=u;
    }
    vis[u]=1;
    for(int i=first2[u]; ~i; i=query[i].next)
    {
        int v=query[i].v;
        if(vis[v])
        {
            int id=query[i].id;
            ans[id]=dis[u]+dis[v]-2*dis[find(v)];
        }
    }
}

void dfs(int u,int len)
{
    vis2[u]=1;
    dis[u]=len;
    for(int i=first[u]; ~i; i=e[i].next)
    {
        int v=e[i].v,w=e[i].w;
        if(!vis2[v])
            dfs(v,len+w);
    }
}
void init()
{
    mem(first,-1);
    mem(first2,-1);
    mem(vis,0);
    mem(vis2,0);
    mem(fa,-1);
    mem(ans,0);
    tot=0;
    tot2=0;
    cnt=1;
    mp.clear();
    for(int i=1; i<=n; i++)
        pre[i]=i;
}
int main()
{
    int t,m;
    int u,v,w;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w);
            add_edge(v,u,w);
        }
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&u,&v);
            add_query(u,v,i);
            add_query(v,u,i);
        }
        dfs(1,0);
        lca(1,1);
        for(int i=1; i<=m; i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值