ZOJ 3261 逆并查集
思路简单代码繁琐一直wa调了一天,真的菜- 因为并查集没有摧毁边的操作,那我们不妨先合并所有边中 不会被摧毁的边,那么就需要我们先保存所有的边,(这里太繁琐容易出错)
- 开一个数组记录每个点的值,因为要找值最大 或者 值相同但点最小的点,在合并的时候根据这个来合并,值小的指向值大的,(值相同时,大点指向小点)
- 这里用了一个技巧就是
map<pair<int,int> , int>
除去被摧毁的边,然后从遍历map合并边.
坑点
- 注意存边的数组大小,
- 初始化和清除
map
, - 每组数据输出一个空行
merge(a,b)
时保证a<b
,这样merge()
规定的顺序才有意义
#include <iostream>
#include <string.h>
#include <map>
#include <string>
using namespace std;
const int maxn = 1e4+5;
int pre[maxn];
int rala[maxn];
int ans[5*maxn];
struct A {
int a,b;
} e[2*maxn];
struct node {
bool f;
int a,b;
} q[5*maxn];
map<pair<int,int>,int> vis;
void init() {
for (int i=0; i<maxn; i++) pre[i] = i;
vis.clear();
}
int find(int x) {
if (x == pre[x]) return x;
return pre[x] = find(pre[x]);
}
void merge(int a,int b) {
int ra = find(a);
int rb = find(b);
if (ra == rb) return ;
if (rala[ra] > rala[rb]) pre[rb] = ra;
else if (rala[ra] < rala[rb]) pre[ra] = rb;
else if (rala[ra] == rala[rb]) {
if (ra > rb) pre[ra] = rb;
else pre[rb] = ra;
}
return ;
}
int n,Q,cnt;
int main() {
// freopen("a.txt","r",stdin);
bool mk=0;
while (scanf("%d",&n) != EOF) {
init();
for (int i=0; i<n; i++) cin >> rala[i];
int k;
cin >> k;
for (int i=0; i<k; i++) {
int a,b;
cin >> a >> b;
if (a > b) swap(a,b);
e[i].a = a;
e[i].b = b;
vis[{a,b}] = 1;
}
cin >> Q;
for (int i=0; i<Q; i++) {
string s;
cin >> s;
if (s == "query") {
int x;
cin >> x;
q[i].a = x;
q[i].f = 0;
}
else {
int a,b;
cin >> a >> b;
if (a>b) swap(a,b);
q[i].a = a;
q[i].b = b;
q[i].f = 1;
vis[{a,b}] = 0;
}
}
map<pair<int,int>,int>::iterator it;
for (it=vis.begin(); it!=vis.end(); it++)
if (it->second)
merge(it->first.first,it->first.second);
cnt = Q;
for (int i=Q-1; i>=0; i--) {
if (q[i].f == 0) { //查询
cnt--;
int cur = q[i].a;
if (rala[find(cur)] > rala[cur] ) ans[cnt] = find(cur);
else ans[cnt] = -1;
}
else merge(q[i].a,q[i].b);
}
if (mk) printf("\n");
else mk = 1;
for (int i=cnt; i<Q; i++)
printf("%d\n",ans[i]);
}
return 0;
}