LCA Tarjin 并查集 离线

以前了解的Tarjin算法是用来求连通分量,在这里是用来求最近公共祖先

并查集用过,很有意思的工具。Kraskal算法是并查集最经典的应用。首先初始化集合,每个元素为单个集合。集合,我们需要选出一个代表,这个代表的性质是set[u] = u。一开始一个集合只有一个元素,所以该集合的代表也就是该元素。当两个集合需要合并时,例如:set[a] = a, set[b] = b。要是把a当作合并后集合的代表,那就让set[b] = a。这样,在find函数中,当发现set[u] != u时,意思就是,u这个元素,是和set[u]这个元素属于同一集合,然后就递归查找,直到set[v] = v,然后回退,更新set[u]。这个步骤叫做“路径压缩”。例如,set[a] = b; set[b]=c; set[c]=d;...., set[y] = z, set[z]=z,当开始find(a)后,会逐步深入到set[z]=z停止,然后回退,令set[y] = z, set[x] = z, set[w] = z, ... set[a] = z,这样,下次再find(a)时,就能很快找到set[z]=z结束,路径是不是变短了呢。

“离线”的意思是,需要预先知道所有的提问。那“在线”的意思就是不需要提前知道问题,你随便问,即刻回答。


先看一下这个参考资料:http://blog.csdn.net/ljsspace/article/details/66910300

这里去掉了参考资料中的ancestor数组。直接用子树的根节点来做集合的代表。从一个节点,走到另一个节点,必然要经过而且最先经过这两个节点的最近公共祖先。所以当发现一个节点A访问了时,马上查询另一个另一个节点B,如果该节点已访问,那B所在集合的代表,就是A和B的最近公共祖先。


#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define M 50
#define C 50
#define Q 50

typedef struct _Node{
	int data;
	int chs;
	struct _Node* ch[C];
}Node, *pNode;

typedef struct _Query{
	int s, e;
	int anc;
}Query, *pQuery;
Query que[Q];

int n, q;
int set[M];
int mk[M];

pNode root;

void init(){
	int i;
	for(i=0; idata = v;
	(*r)->chs = c;
	for(i=0; ich[i]));
	}
}

void show(pNode r){
	int i;
	printf("%d ", r->data);
	for(i=0; ichs; i++)
		show(r->ch[i]);
}

void LCA(pNode r){
	int i;
	int t;
	for(i=0; ichs; i++){
		LCA(r->ch[i]);
		combine(r->data, r->ch[i]->data);
	}
	mk[r->data] = 1;
	for(i=0; idata==que[i].s && mk[que[i].e])
			t = que[i].e;
		else if(r->data==que[i].e && mk[que[i].s])
			t = que[i].s;
		if(t!=-1)
			que[i].anc = find(t);
	}
}

void main(){
	int i;
	freopen("in.txt", "r", stdin);
	while(scanf("%d %d", &n, &q)!=EOF){
		init();
		root = 0;
		create(&root);

		for(i=0; i

数据:

20 19
0 2
1 3
3 0
4 2
8 2
12 0
13 0
9 2
14 0
15 0
5 2
10 2
16 0
17 0
11 2
18 0
19 0
2 2
6 0
7 0
8 5
12 14
14 6
6 7
10 9
3 10
14 15
15 8
11 10
13 12
5 4
18 13
4 17
16 19
9 19
10 19
7 17
17 16
18 19

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