ZOJ 3261 Connections in Galaxy War 逆向并查集/删边

博客介绍了如何利用并查集和离线处理解决ZOJ3261竞赛题目。题目涉及图论中的集合合并与删除边的操作,并要求在每次操作后查询集合中能量最大的顶点。博主提出先获取所有查询,然后逆向处理删除边的操作,同时在合并集合时考虑能量值和顶点编号,确保集合中能量最大且编号最小的顶点作为集合代表。代码实现包括并查集的初始化、合并及查询操作,以及离线处理的策略。
摘要由CSDN通过智能技术生成

 ZOJ3261

题意:给出n个顶点,每个顶点有对应的能量值,再给出m个顶点对表示初始图的联通情况。

最后给出Q个条件,query表示 查询某个顶点所在集合中能量最大的顶点编号(如果最大能量的顶点有相同则输出序号最小的)。destroy+顶点对表示删除该顶点之间的联通。

思路:

并查集做不到合并完然后删边,所以想到先得到查询数据然后根据删的边逆向并查集,每次删的边变为加上该边,就可以使用合并操作了。既然是逆向离线处理,所以创建初始图的时候如果该边会被删除就不要加入初始图里。

最后是合并操作,题目要求输出每个集合中能量最大的顶点,如果能量最大的有多个就输出编号小的,这个条件可以放到并查集合并操作中处理——合并祖先顶点的时候优先把能量值大的顶点当作祖先顶点,小的那个合并进去。如果能量值相同则优先编号小的当作祖先。

#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
#define ll long long
#define int ll
#pragma warning (disable:4996);
const int N = 2e5 + 10;
int T, n,m,Q;
int pw[N];
int pre[N];
struct node {
    int op, first, second;
}q[N];//记录选择,离线处理,e数组记录起始的图
pair<int, int>e[N];
int fnd(int k) {
    return pre[k] == k ? k : pre[k] = fnd(pre[k]);
}
void join(int a, int b) {//满足题目规则的合并
    int t1 = fnd(a), t2 = fnd(b);
    if (t1 != t2) {
        if(pw[t1]==pw[t2])//集合中最大能量根节点相同,选择序号最小的
            pre[max(t1, t2)] = min(t1, t2);//max并入min里
        else if (pw[t1] > pw[t2]) {
            pre[t2] = t1;//能量小的并入能力大的
        }
        else {
            pre[t1] = t2;
        }
    }
}
void init(int n) {
    for (int j = 0; j < n; j++)
        pre[j] = j;
}
map<pair<int, int>, int> M;//判断顶点对是否存在
deque<int>ans;

signed main()
{ ios::sync_with_stdio(false); cout.tie(NULL);
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
#endif // !ONLINE_JUDGE
    int f = 1;
    while (cin >> n) {
        M.clear();//每个样例初始化
        init(n);
        for (int j = 0; j < n; j++) {
            cin >> pw[j];
        }
        cin >> m;
        for (int j = 0; j < m; j++) {
            cin >> e[j].first >> e[j].second;
            if (e[j].first > e[j].second) swap(e[j].first, e[j].second);
            //同一边次序,编号小的在前
        }
        cin >> Q;//选择次数
        for (int j = 0; j < Q; j++) {//记录选择
            string s;
            cin >> s;
            if (s[0] == 'q') {
                q[j].op = 1;
                cin >> q[j].first;
            }
            else {
                q[j].op = 0;
                cin >> q[j].first>>q[j].second;
                if (q[j].first > q[j].second) swap(q[j].first, q[j].second);
                M[make_pair(q[j].first,q[j].second)]=1;//标记该边会被摧毁,初始建图不能加上
            }
        }
        for (int j = 0; j < m; j++) {
            if (!M[e[j]]) {//该边不会被摧毁
                join(e[j].first, e[j].second);//建立初始集合
            }
        }
        
        for (int j = Q-1; ~j; --j) {//反向查询
            if (!q[j].op) {//加边
                join(q[j].first, q[j].second);
            }
            else {
                int t = fnd(q[j].first);
                if (pw[t] > pw[q[j].first]) {//防止出现一个元素的集合
                    ans.push_back(t);
                }
                else {
                    ans.push_back(-1);
                }
            }
            
        }
        if (f) f = 0;//格式好烦人
        else cout << endl;

        while (!ans.empty()) {
            cout << ans.back() << endl;
            ans.pop_back();
        }
    } 
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值