20211229[按秩合并并查集、最小生成树][BZOJ4668]冷战
题意:给定N点,动态加边与询问两点最早是哪条边开始连通,强制在线
首先如果离线的话可以直接跑最小生成树,不过代码不好处理。
当然可以选择二分+并查集判连通,不过只有60分
正解是不进行压缩路径的并查集进行合并,并且将加入时间作为边权,查询的话可以暴力跑两个点之间的最大边权即可。
但是在做的时候还是想过有没有合适的数据结构可以预维护并查集内的点集还有两点间的关系,因为这样的话查询说不定可以避免O(n),想过vector,但是50W的数据显然不能用二维数组存储连通关系,不知道有没有什么巨佬方法可以做到。
#include<cstdio>
#define maxn 500039
#define max(a, b) ((a)>(b)?(a):(b))
using namespace std;
int N, M, ans, f[maxn] ,h[maxn], cnt, w[maxn], cnt2, g[maxn], root;
int find(int x){return f[x]==x?x:find(f[x]);}
int main(){
freopen("1.in", "r", stdin);
scanf("%d%d", &N, &M);
int opt, u, v, x, y;
for(int i = 1; i < N+1; i++){
h[i] = 1;
f[i] = i;
}
while(M--){
scanf("%d%d%d", &opt, &u, &v);
cnt2++;
u^=ans; v^=ans;
if(opt){
x = find(u), y = find(v);
int maxx = 0;
if(x!=y)
printf("0\n");
else{
x = u, y = v;
while(g[u]!=cnt2){ //找祖先,很蠢的做法,常熟较大
g[u]=cnt2;
u = f[u];
}
while(g[v]!=cnt2){
v = f[v];
}
root = v;
u = x, v = y;
while(u!=root){ //跑最大值
maxx = max(maxx, w[u]);
u = f[u];
}
while(v!=root){
maxx = max(maxx, w[v]);
v = f[v];
}
printf("%d\n", maxx);
}
ans = maxx;
}
else{
cnt++;
x = find(u), y = find(v);
if(x!=y){
if(h[x]<h[y]){ //按秩合并
f[x] = y;
h[y] += h[x];
w[x] = cnt;
}
else{
f[y] = x;
h[x] += h[y];
w[y] = cnt;
}
}
}
}
return 0;
}