题意:
有n个星球(编号 0 ~ n ),每个星球都有自己的power值,有m条线连接着星球,接下来有q个操作要求如下:
"destroy a b" 破坏a与b之间的连接。
"query a" 询问与a星球直接或间接连接的星球中power值最大的星球的编号,如果power值相同,则输出最小的编号。
题解:
思路:这题看到直接或间接连接就想到并查集,但是并查集没办法维护删除边的操作,怎么办呢?这时就要用到并查集的离线逆向处理了。也就是先记录下所有的操作,然后从后往前处理,原先的删除边操作就变成了加入集合的操作了。然后就可以利用并查集处理这样的问题了。
具体操作:把原先的所有连接的边和操作都用结构体数组保存下来,然后用一个map记录下来哪些边是被破坏了的,这样就可以得到破坏完了之后的连接情况了。接着从后往前处理每遇到一个破坏操作就将两个点加入集合,加入集合的函数需要处理一下使得每个点的祖先节点要么是他自己要么是符合条件的节点(或者是与自己相等权值的节点,之后选择一下即可,因为这里WA了n发,检讨检讨)。将答案存在数组里面输出即可。具体看代码。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<cstdlib>
#include<string>
#define INF 0x3f3f3f3f
#define MAXM 20000 + 10
#define MAXN 100000 + 10
using namespace std;
typedef unsigned long long ll;
const ll MOD = 1e9 + 7;
struct EDGE{
int a, b;
}edge[MAXN];
struct OP{
char s[10];
int a;
int b;
}op[MAXN];
int father[MAXN];
int power[MAXN];
int ans[MAXN];
map<pair<int, int>, int> vis;
int get_root(int x)
{
if(father[x] == x) return x;
return father[x] = get_root(father[x]);
}
void unions(int x, int y)
{
int root_x = get_root(x);
int root_y = get_root(y);
if(root_x != root_y){
int root;
if(power[root_x] == power[root_y])
root = min(root_x, root_y);
else if(power[root_x] > power[root_y])
root = root_x;
else
root = root_y;
father[root_x] = root;
father[root_y] = root;
}
}
int main()
{
int n;
bool flag = false;
while(~scanf("%d", &n)){
vis.clear();
memset(edge, 0, sizeof(edge));
memset(op, 0, sizeof(op));
memset(father, 0, sizeof(father));
memset(power, 0, sizeof(power));
memset(ans, 0, sizeof(ans));
for(int i = 0; i < n; i ++){
father[i] = i;
int p; scanf("%d", &p);
power[i] = p;
}
int m; scanf("%d", &m);
for(int i = 0; i < m; i ++)
scanf("%d %d", &edge[i].a, &edge[i].b);
int q; scanf("%d", &q);
for(int i = 0; i < q; i ++){
scanf("%s", op[i].s);
if(op[i].s[0] == 'q')
scanf("%d", &op[i].a);
if(op[i].s[0] == 'd'){
scanf("%d %d", &op[i].a, &op[i].b);
pair<int, int> temp = make_pair(op[i].a, op[i].b);
vis[temp] = 1;
temp = make_pair(op[i].b, op[i].a);
vis[temp] = 1;
}
}
for(int i = 0; i < m; i ++){
pair<int, int> temp = make_pair(edge[i].a, edge[i].b);
if(!vis[temp])
unions(edge[i].a, edge[i].b);
}
int cnt = 0;
for(int i = q - 1; i >= 0; i --){
if(op[i].s[0] == 'q'){
ans[cnt] = get_root(op[i].a);
if(power[ans[cnt]] <= power[op[i].a]) ans[cnt] = -1;
cnt ++;
}
if(op[i].s[0] == 'd'){
unions(op[i].a, op[i].b);
}
}
if(flag) printf("\n");
else flag = true;
for(int i = cnt - 1; i >= 0; i --) printf("%d\n", ans[i]);
}
}
/*
The WAM is F**KING interesting .
*/
本文介绍了一种使用并查集解决涉及直接或间接连接的星球查询问题的方法。通过离线逆向处理,即将删除边的操作转换为加入集合的操作,解决了并查集无法直接支持边删除的问题。文章详细描述了实现过程,并提供了完整的代码示例。
835

被折叠的 条评论
为什么被折叠?



