ZOJ ~ 3261 ~ Connections in Galaxy War (逆序并查集 + map)

题意:星球大战,每个星球会有一个武力值,星球间有一些通道相连,直接或间接相连的星球可以互相求助(当然求助最厉害的星球啦)。先输入N表示有N个星球,编号为0~(n-1),然后输入M表示有M组关系,表示两个星球之间有通道。然后有Q次操作,操作有两种,

1.query a:问a能求助的最厉害的星球是哪个

2.destroy a b:a和b通道被摧毁了

对于每个query操作输出一个能求助到的最厉害的星球的编号,如果最厉害的有多个输出编号最小的那个,如果没有星球比他更厉害(即没有星球的武力值严格大于他)那么就输出-1。多组输出输出,每组数据后面输出一个空行,最后一组后面不输出空行。

思路:逆序并查集。由于并查集不能进行集合的拆离操作,所以逆向思考那么拆离操作就变成了合并操作。所以先进行离线操作,即把不会被摧毁的边都先建立起来,然后反向处理,合并的时候按武力值得大小合并,把小的合并到大的上,相同的时候把编号大的合并到编号小的上,把答案存到起来输出就可以了。

坑点:①a,b大小不一定,就是一开始告诉你1 2有关系,然后会有摧毁2 1的操作②格式控制,最后一行后面没有空行,其他都有


#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10005;
const int MAXQ = 50005;
int n, m, q;
struct Star
{
    int power, pre;//武力值,父节点编号
}star[MAXN];//星球信息
struct in
{
    int a, b;
    char c;//操作
}in[MAXQ];//输入
int ans[MAXQ];//答案
map<pair<int, int>, bool> M;
int find(int x)
{
    if (star[x].pre == x) return x;
    star[x].pre = find(star[x].pre);
    return star[x].pre;
}
void Union(int a, int b)
{
    int root1 = find(a), root2 = find(b);
    if (star[root1].power < star[root2].power) star[root1].pre = root2;
    else if(star[root1].power > star[root2].power) star[root2].pre = root1;
    else star[max(root1, root2)].pre = min(root1, root2);
}
int main()
{
    bool has_out = false;
    while (~scanf("%d", &n))
    {
        M.clear();
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &star[i].power);
            star[i].pre = i;//初始化
        }
        scanf("%d", &m);
        for (int i = 0; i < m; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            M[make_pair(min(a, b), max(a, b))] = true;//需要建边
        }
        scanf("%d", &q);
        string op;
        for (int i = 0; i < q; i++)//操作保存到in
        {
            cin >> op;
            in[i].c = op[0];
            if (op[0] == 'q') scanf("%d", &in[i].a);
            else if(op[0] == 'd')
            {
                scanf("%d%d", &in[i].a, &in[i].b);
                M[make_pair(min(in[i].a, in[i].b), max(in[i].a, in[i].b))] = false;//被破坏的先不建边
            }
        }
        //for (auto i: M) if (i.second) Union((i.first).first, (i.first).second);
        for (map<pair<int, int>, bool>::iterator it = M.begin(); it != M.end(); it++)//建立关系
        {
            if ((*it).second) Union((*it).first.first, (*it).first.second);
        }
        int cnt = 0;
        for (int i = q - 1; i >= 0; i--)//逆序处理操作
        {
            if (in[i].c == 'q')
            {
                int t = find(in[i].a);
                if (star[t].power > star[in[i].a].power) ans[cnt++] = t;//可以求助即根节点武力值大于自己的武力值
                else ans[cnt++] = -1;
            }
            else if(in[i].c == 'd') Union(in[i].a, in[i].b);//修复边
        }
        if(has_out) printf("\n");//格式控制
        has_out = true;
        for (int i = cnt - 1; i >= 0; i--) printf("%d\n", ans[i]);//输出答案
    }
    return 0;
}
/*
2
10 20
1
0 1
5
query 0
query 1
destroy 0 1
query 0
query 1

5
30 20 10 1 1
2
1 2
2 3
5
q 3
q 4
q 1
d 1 3
q 3
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值