题目链接在这里
题目大意
n个星球,每个星球有一个防御值,一共有m条路,连接x星球和y星球。现在发生星际战争了,a星球要寻求帮助,它只能寻求比它的防御值大的星球的帮助(相同的防御值的话取下标较小的那个),其中会破坏道路再进行寻求帮助。问每次询问时,某星球该找哪个星球寻求帮助。
思路
这是一个并查集问题,如果顺着题目的思路来的话,要进行拆边。但是并查集没有拆边的功能,所以我们转换思路,先处理最后的询问,然后依次往前进行路的合并。这样就简单一点。
代码如下
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#define rep(i, x) for(int i = 0; i < x; ++i)
#define clr(x) memset(x, 0, sizeof(x))
using namespace std;
const int MaxN = 10010;
const int MaxM = 20010;
const int MaxQ = 50010;
struct Query{
bool flag;
int x, y;
}query[MaxQ];
struct Edge{
int y, nxt;
}edge[MaxM * 2];
int n, m, q;
int r[MaxN], par[MaxN];
int head[MaxN], tol;
int x[MaxM], y[MaxM];
int ans[MaxQ], cnt;
void addEdge(int x, int y){
edge[tol].y = y;
edge[tol].nxt = head[x];
head[x] = tol++;
}
void init(){
rep(i, MaxN) par[i] = i;
memset(head, -1, sizeof(head));
tol = 0;
cnt = 0;
}
int Find(int x){
if(x == par[x]) return x;
return par[x] = Find(par[x]);
}
bool unite(int x, int y){
x = Find(x);
y = Find(y);
if(x == y) return false;
if(r[x] > r[y] || r[x] == r[y] && x < y)
par[y] = x;
else
par[x] = y;
return true;
}
void solve(){
rep(i, m){
int xx = x[i];
int yy = y[i];
bool flag = false;
for(int j = head[xx]; j != -1; j = edge[j].nxt){
if(edge[j].y == yy){
flag = true;
break;
}
}
if(flag) continue;
unite(xx, yy);
}
for(int i = q - 1; i >= 0; --i){
if(query[i].flag){
unite(query[i].x, query[i].y);
}
else{
int x = query[i].x;
int fx = Find(x);
if(r[fx] <= r[x]) ans[cnt++] = -1;
else ans[cnt++] = fx;
}
}
for(int i = cnt - 1; i >= 0; --i)
printf("%d\n", ans[i]);
}
int main(){
int tag = 0;
while(~scanf("%d", &n)){
init();
if(tag++) printf("\n");
rep(i, n)
scanf("%d", &r[i]);
scanf("%d", &m);
rep(i, m){
scanf("%d%d", &x[i], &y[i]);
}
scanf("%d", &q);
char str[10];
rep(i, q){
scanf("%s", str);
if(strcmp(str, "query") == 0){
query[i].flag = false;
scanf("%d", &query[i].x);
}
else{
query[i].flag = true;
scanf("%d%d", &query[i].x, &query[i].y);
addEdge(query[i].x, query[i].y);
addEdge(query[i].y, query[i].x);
}
}
solve();
}
return 0;
}