题解
题目链接
很明显,对于题目所给的操作需要使用并查集处理,但是普通的并查集压缩路径会破坏已有数据,不压缩路径会超时。
使用启发式合并并查集求解,在根节点记录当前子树深度,合并时将深度小的作为深度大的子树,这样能保证复杂度为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;
}