最近迷上了一个叫splatoon的游戏呢
最近做了几道并查集的题,完全被虐成狗了。。。才明白如果并查集考灵活会这么难啊。。。
3319: 黑白树
Time Limit: 10 Sec Memory Limit: 512 MB
Description
给定一棵树,边的颜色为黑或白,初始时全部为白色。维护两个操作:
1.查询u到根路径上的第一条黑色边的标号。
2.将u到v 路径上的所有边的颜色设为黑色。
Notice:这棵树的根节点为1
Input
第一行两个数n,m分别表示点数和操作数。
接下来n-? 1行,每行2个数u,v.表示一条u到v的边。
接下来m行,每行为以下格式:
1 v 表示第一个操作
2 v u 表示第二种操作
Output
对于每个询问,输出相应答案。如果不存在,输出0。
Sample Input
5 4
1 2
1 3
2 4
2 5
1 2
2 2 3
1 3
1 4
Sample Output
0
2
1
HINT
对于 100% 的数据:n,m<=10^6
Source
[Submit][Status][Discuss]
这道题目选用并查集去解决其实是一个非常巧妙的思路,最重要的是并查集的几个很有趣的性质:
【1】一个元素集合的所有元素的答案一样
【2】修改一个元素集合的值只用修改其带头元素(parent)的值即可
题解:
先用并查集将所有最终为黑边的边的端点合并 , 并记录下该条边最早变为黑边的时间 ; 然后反着来 , 将最终仍未被黑边连接的点用并查集合并起来 ,(此处一个集合的元素满足其中的所有点都是被白边所连接), 求出该集合的答案(如【1】所说 , 同一集合的元素的答案相同),然后反着来,不断更新白点,合并点集(有些黑边会被消去),维护当前的答案(利用【2】);
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 2100001
using namespace std;
int n , m , cnt;
int inv[M] , inv_x[M] , inv_y[M] , inv_num[M];
int ind[M] , nex[M] , e[M] , pot[M] , tot;
int q_typ[M] , q_x[M] , q_y[M];
int f[M] , fa[M];
int deep[M];
int ans[M];
int pb[M];
int find(int x){return f[x] == x ? x : f[x] = find(f[x]);}
void add(int a , int b , int c){
nex[++tot] = ind[a];
e[ind[a] = tot] = b;
inv_num[tot] = c;
}
void dfs(int v , int u){
fa[v] = u;
deep[v] = deep[u] + 1;
for(int i = ind[v] ; i ; i = nex[i]){
if(e[i] == u)continue;
inv[e[i]] = i;
inv_x[i] = v;
inv_y[i] = e[i];
dfs(e[i] , v);
}
}
void change(int x , int y , int opt){
x = find(x) , y = find(y);
while(x != y){
if(deep[x] < deep[y])swap(x , y);
if(!pot[x])pot[x] = opt , f[x] = f[fa[x]];
x = f[fa[x]];
}
}
void solve(int x , int y , int opt){
x = find(x) , y = find(y);
while(x != y){
if(deep[x] < deep[y])swap(x , y);
if(pot[x] == opt) f[x] = f[fa[x]];
x = fa[x];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1 ; i < n ; ++i){
int u , v;
scanf("%d%d",&u,&v);
add(u , v , i);
add(v , u , i);
}
dfs(1 , 0);
for(int i = 1 ; i <= n ; ++i)f[i] = i;
for(int i = 1 ; i <= m ; ++i){
scanf("%d%d",&q_typ[i],&q_x[i]);
if(q_typ[i] == 2){
scanf("%d",&q_y[i]);
change(q_x[i] , q_y[i] , i);
}
}
for(int i = 1 ; i <= n ; ++i)f[i] = i;
for(int i = 2 ; i <= n ; ++i){
if(!pot[i]){
int x = find(inv_x[inv[i]]) , y = find(inv_y[inv[i]]);
if(x == y)continue;
if(deep[x] > deep[y])swap(x , y);
f[y] = x;
}
}
for(int i = m ; i >= 1 ; --i){
if(q_typ[i] == 1)ans[++cnt] = inv_num[inv[find(q_x[i])]];
else solve(q_x[i] , q_y[i] , i);
}
for(int i = cnt ; i ; --i)printf("%d\n",ans[i]);
}