道路修建 EXT BNUOJ - 51275 并查集 启发式合并

题解

题目链接
很明显,对于题目所给的操作需要使用并查集处理,但是普通的并查集压缩路径会破坏已有数据,不压缩路径会超时。
使用启发式合并并查集求解,在根节点记录当前子树深度,合并时将深度小的作为深度大的子树,这样能保证复杂度为logn。
处理过程中顺便顺便记录每条边建立的时间,对于1号操作当uv同属一个根时,则两点可联通。
根据当前点深度优先跳深度深的节点(类似于lca不过直接跳就行深度最大为logn),直到两个点相遇,过程中建立时间取max。

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int vex[N], dep[N], tem[N]; //树深度 父节点边建立的时间

int find(int x)
{
	return vex[x] == x ? x : find(vex[x]); //不压缩路径
}
bool join(int x, int y, int z)
{
	x = find(x), y = find(y); //得到根
	if (x == y)
		return 0; //没加边
	if (dep[x] > dep[y])
		swap(x, y);
	vex[x] = y; //小的接在大的下面
	tem[x] = z; //记录连接时间
	if (dep[x] == dep[y])
		dep[y]++; //两个相等则合并后深度+1
	return 1;
}
int jump(int x, int y) //得到xy最早连通时间
{
	if (x == y) //相汇
		return 0;
	if (dep[x] < dep[y]) //只跳小的
		return max(jump(vex[x], y), tem[x]); //路径取max
	if (dep[x] > dep[y])
		return max(jump(x, vex[y]), tem[y]);
	return max(jump(vex[x], vex[y]), max(tem[x], tem[y])); //相同一起跳
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int T;
	cin >> T;
	while (T--)
	{
		int n, m;
		cin >> n >> m;
		for (int i = 1; i <= n; ++i)
			vex[i] = i, dep[i] = 1, tem[i] = 0;
		int last = 0, blok = n;
		for (int i = 1; i <= m; i++)
		{
			int t, u, v;
			scanf("%d%d%d", &t, &u, &v);
			u = (u ^ last) % n + 1, v = (v ^ last) % n + 1; //解密
			if (!t)
				blok -= join(u, v, i), last = blok;
			else
			{
				last = 0;
				if (find(u) == find(v)) //当前连通
					last = jump(u, v);
			}
			printf("%d\n", last);
		}
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/CaprYang/article/details/87984432
今日推荐