quailty's Contest #1 道路修建 EXT(启发式合并)

题目链接  道路修建 EXT

考虑并查集的启发式合并,合并的时候小的子树的根成为大的子树的根的儿子。

可以证明这样整棵树的深度不会超过$logn$。

两个根合并的时候,产生的新的边的边权为当前的时间。

那么询问的时候答案就为$x$到$y$的最短路径上的所有边的边权最大值。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N = 5e5 + 10;

int T;
int n, m;
int num;
int ans;
int father[N], c[N], root[N], deep[N], sz[N];
int now;
unordered_set <int> s[N];

int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &m);
		num = n;
		ans = 0;
		rep(i, 0, n + 1){
			s[i].clear();
			s[i].insert(i);
		}

		rep(i, 1, n){
			c[i] = 0;
			sz[i] = 1;
			father[i] = i;
			deep[i] = 1;
			root[i] = i;
		}

		rep(i, 1, m){
			int op, x, y;
			scanf("%d%d%d", &op, &x, &y);
			x ^= ans;
			y ^= ans;

			if (op == 0){
				if (root[x] == root[y]){
					printf("%d\n", ans = num);
					continue;
				}

				--num;
				int fx = root[x], fy = root[y];
				if (sz[fx] < sz[fy]){
					swap(fx, fy);
					swap(x, y);
				}

				c[fy] = i;

				sz[fx] += sz[fy];
				father[fy] = fx;
				sz[fy] = 0;
				for (auto u : s[fy]){
					root[u] = fx;
					++deep[u];
					s[fx].insert(u);
				}

				s[fy].clear();
				printf("%d\n", ans = num);			
			}

			else{
				if (root[x] != root[y]){
					printf("%d\n", ans = 0);
					continue;
				}

				now = 0;
				if (deep[x] < deep[y]) swap(x, y);
				while (deep[x] != deep[y]){
					now = max(now, c[x]);
					x   = father[x];
				}

				while (true){
					if (x == y) break;
					now = max(now, c[x]);
					now = max(now, c[y]);
					x = father[x];
					y = father[y];
				}

				printf("%d\n", ans = now);
			}
		}
	}

	return 0;
}

  

posted @ 2018-03-19 22:40  cxhscst2  阅读(240)  评论(0编辑  收藏  举报