ZOJ - 3261 Connections in Galaxy War (逆向并查集 离线操作)

题目链接

题意:

有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 .

*/

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值