URAL 1471(lca tarjan算法)

题意:给定一棵树,查询时给定两个点,求出两个点的距离。

暴力做肯定超时的。我的做法是采用lca(最近公共祖先)的离线算法,即tarjan算法(据说Tarjan提出了很多算法,可能还有很多tarjan算法),算法里用到了并查集。在输入完所有查询之后,在求出答案。tarjan算法的做法是:一开始vis数组初始化为0,从树根开始递归往下对点进行染色,刚到一个点的时候将vis取为-1,在继续递归;遍历完子节点返回之后vis变为1。在vis变为1之前,检索一下当前节点的所有查询,设查询中的另外一个节点为To,如果vis[To]==0,就continue,因为To还没有处理,不知道它的信息;如果vis[To]==-1,说明To被访问了一次,但是还没有返回到,这意味着To是当前节点的祖先,因此To就是当前节点的最近公共祖先;如果vis[To]==1,说明To已经处理完了,这时候并查集就派上用场了。在递归时,当一个节点处理完返回到父亲那里时,就把父亲变成其所在集合的代表元素。在刚才讨论到vis[To]==1的情况中,可以知道find(To)(即To所在集合的代表元素)就是To和当前节点的最近公共祖先了(这个可以画图演算一下)。在这道题中,我们一开始可以用一个简单的递归算出每个点到根节点的距离dis[i]。那么对于一个查询的两个点fir和sec,它们的距离就是dis[fir]-dis[lca]+dis[sec]-dis[lca],lca是fir和sec的最近公共祖先。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<climits>
#include<queue>
#include<vector>
#include<map>
using namespace std;

struct node
{
	int to,id;
	node(int t,int i)
	{
		to=t;
		id=i;
	}
	node(){}
};
const int maxn=50005;
vector<node>vec[maxn];
vector<pair<int,int>>query;
int father[maxn],fir[maxn<<1],nxt[maxn<<1],vv[maxn<<1],val[maxn<<1],dis[maxn],ans[75005],e;
int vis[maxn];//0 means it's white,-1 means it's grey, 1 means it's black

int findn(int n)
{
	if(n!=father[n]) father[n]=findn(father[n]);
	return father[n];
}

void add(int a,int b,int c,int i)
{
	vv[e]=b;
	val[e]=c;
	nxt[e]=fir[a];
	fir[a]=e++;
}

void get_height(int sroot,int dist)
{
	vis[sroot]=1;
	dis[sroot]=dist;
	for(int i=fir[sroot];i!=-1;i=nxt[i])
	{
		int v=vv[i];
		if(!vis[v])
		{
			get_height(v,dist+val[i]);
		}
	}
}

void dfs(int cur,int fa)
{
	vis[cur]=-1;
	for(int i=fir[cur];i!=-1;i=nxt[i])
	{
		int v=vv[i];
		if(!vis[v])
		{
			dfs(v,cur);
			father[v]=cur;
		}
	}
	int size=vec[cur].size();

	for(int i=0;i<size;i++)
	{
		node nxt=vec[cur][i];
		if(!vis[nxt.to]) continue;
		if(-1==vis[nxt.to])
		{
			ans[nxt.id]=nxt.to;
		}
		else if(1==vis[nxt.to])
		{
			ans[nxt.id]=findn(nxt.to);
		}
	}
	vis[cur]=1;
}
int main()
{
	#pragma comment(linker, "/STACK:102400000,102400000")//此代码需要扩栈,可能在递归时耗的内存有点大
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		for(int i=0;i<=n;i++)
		{
			father[i]=i;
			fir[i]=-1;
			vis[i]=0;
			vec[i].clear();
		}
		e=0;//important
		int a,b,c;
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,c,i);
			add(b,a,c,i);
		}
		get_height(0,0);
		int q;
		scanf("%d",&q);
		for(int i=0;i<q;i++)
		{
			scanf("%d%d",&a,&b);
			vec[a].push_back(node(b,i));
			vec[b].push_back(node(a,i));
			query.push_back(make_pair<int,int>(a,b));
		}
		for(int i=0;i<=n;i++) vis[i]=0;
		dfs(0,0);
		int size=query.size();
		for(int i=0;i<size;i++)
		{
			int fir=query[i].first;
			int sec=query[i].second;
			int lca=ans[i];
			int distance=abs(dis[lca]-dis[fir])+abs(dis[lca]-dis[sec]);
			printf("%d\n",distance);
		}
	}
}
时间: 2024-10-29 16:06:57

URAL 1471(lca tarjan算法)的相关文章

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

HDU 2586 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): 6422    Accepted Submission(s): 2411 Problem Description There are n houses in the village and some bidirectional roads connecting

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

【POJ 1330 Nearest Common Ancestors】LCA Tarjan算法

题目链接:http://poj.org/problem?id=1330 题意:给定一个n个节点的有根树,以及树中的两个节点u,v,求u,v的最近公共祖先. 数据范围:n [2, 10000] 思路:从树根出发进行后序深度优先遍历,设置vis数组实时记录是否已被访问. 每遍历完一棵子树r,把它并入以r的父节点p为代表元的集合.这时判断p是不是所要求的u, v节点之一,如果r==u,且v已访问过,则lca(u, v)必为v所属集合的代表元.p==v的情况类似. 我的第一道LCA问题的Tarjan算法

POJ 1330 Nearest Common Ancestors(LCA Tarjan算法)

题目链接:http://poj.org/problem?id=1330 题意:给定一个n个节点的有根树,以及树中的两个节点u,v,求u,v的最近公共祖先. 数据范围:n [2, 10000] 思路:从树根出发进行后序深度优先遍历,设置vis数组实时记录是否已被访问. 每遍历完一棵子树r,把它并入以r的父节点p为代表元的集合.这时判断p是不是所要求的u, v节点之一,如果r==u,且v已访问过,则lca(u, v)必为v所属集合的代表元.p==v的情况类似. 我的第一道LCA问题的Tarjan算法

Tarjan 算法求 LCA / Tarjan 算法求强连通分量

[时光蒸汽喵带你做专题]最近公共祖先 LCA (Lowest Common Ancestors)_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili tarjan LCA - YouTube Tarjan算法_LCA - A_Bo的博客 - CSDN博客 Tarjan离线算法求最近公共祖先(LCA) - 初学者 - CSDN博客 最近公共祖先(LCA) - riteme.site Fuzhou University OnlineJudge P3379 [模板]最近公共祖先(LCA) - 洛谷 |

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问

LA 5061 LCA tarjan 算法

题目大意: 给定所有点的权值都为0,给定一棵树以后,每次询问都要求给定两点 x , y 和一个权值w,要求x,y路径上所有点权值加上w,最后求出每一个节点的值 这里因为查询和点都特别多,所以希望能最后一次性更新节点的值 我们可以这么考虑,每次询问中找到x,y的最近公共祖先,那么我们将val[x] +=w , val[y]+=w , val[lca]-=w; 最后做dfs的时候,不断自底向上更新val值,让父亲加上所有儿子的val值,那么lca减掉了一个w,最后2端会加上两个w,最后还是相当于加了

HDU 2586 LCA离线算法 tarjan算法

LCA tarjan算法模板题 题意:给一个无根树,有q个询问,每个询问两个点,问两点的距离. 用tarjan离线算法算出每个询问的两点的最近公共祖先 ans[i]=dis[x[i]]+dis[y[i]]-2*dis[z[i]]; //  x[i],y[i]分别存储每次询问的两点,z[i]存储这两点的最近公共祖先 #include "stdio.h" #include "string.h" int tot,n,m; int f[40010],x[40010],y[4