ZOJ3261 Connections in Galaxy War(逆向并查集)

题目传送门 zoj3261

In order to strengthen the defense ability, many stars in galaxy allied together and built many bidirectional tunnels to exchange messages. However, when the Galaxy War began, some tunnels were destroyed by the monsters from another dimension. Then many problems were raised when some of the stars wanted to seek help from the others.

In the galaxy, the stars are numbered from 0 to N-1 and their power was marked by a non-negative integer pi. When the star A wanted to seek help, it would send the message to the star with the largest power which was connected with star A directly or indirectly. In addition, this star should be more powerful than the star A. If there were more than one star which had the same largest power, then the one with the smallest serial number was chosen. And therefore, sometimes star A couldn’t find such star for help.

Given the information of the war and the queries about some particular stars, for each query, please find out whether this star could seek another star for help and which star should be chosen.

Input:

There are no more than 20 cases. Process to the end of file.

For each cases, the first line contains an integer N (1 <= N <= 10000), which is the number of stars. The second line contains N integers p0, p1, … , pn-1 (0 <= pi <= 1000000000), representing the power of the i-th star. Then the third line is a single integer M (0 <= M <= 20000), that is the number of tunnels built before the war. Then M lines follows. Each line has two integers a, b (0 <= a, b <= N - 1, a != b), which means star a and star b has a connection tunnel. It’s guaranteed that each connection will only be described once.

In the (M + 2)-th line is an integer Q (0 <= Q <= 50000) which is the number of the information and queries. In the following Q lines, each line will be written in one of next two formats.

  • "destroy a b" - the connection between star a and star b was destroyed by the monsters. It’s guaranteed that the connection between star a and star bwas available before the monsters’ attack.

  • "query a" - star a wanted to know which star it should turn to for help

There is a blank line between consecutive cases.

Output:

For each query in the input, if there is no star that star a can turn to for help, then output “-1”; otherwise, output the serial number of the chosen star.

Print a blank line between consecutive cases.

Sample Input:
2
10 20
1
0 1
5
query 0
query 1
destroy 0 1
query 0
query 1
Sample Output:
1
-1
-1
-1

题意:

多组输入,给定N个点,每个点有一个权值,并且给定M条双向边

然后有Q次操作,操作有两种类型:

query a:查找与点a联通的某个点,且该点在这个连通分量的权值最大,若该点的权值大于a,则输出它的下标,否则输出-1

destory a b:摧毁点a和点b之间的双向边

思路:

对于普通的并查集而言,是无法完成删除边的操作的,所以这题是一道 “拆查集”

正向得完成这道题十分麻烦,那么,如果倒着来呢?

先读入所有的点和所有的边,再离线得读入所有的操作

如果为摧毁操作,那么我就把这条边删除掉,

最后将所有的操作倒过来完成时,原先的摧毁操作,就变成了合并操作了

拆查集也就变成了并查集,按正常的操作来走就行了

那具体该怎么写代码呢,首先需要记录下所有的边,然后离线读入的时候再删除这些边

那么就可以利用set+pair的组合拳,把每条边扔进set里头

当离线读入完成之后,再对set里的所有边的点进行合并的操作,

而合并时需要将权值较大的点作为根节点,所以需要比较俩个点的根节点(按秩合并)

然后在find函数里头做普通的路径压缩即可

最后再倒序进行操作的时候,对于query操作,访问需查询节点的根节点的权值

若它比自身的节点的权值要大,那么就记录根节点的下标,否则记录-1

对于delete操作,直接按照上述方法合并俩个点即可

最后将记录下来的答案,再倒序输出即可

完结。


代码
#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e4+5;
const int MAXQ = 5e4+5;
struct ndoe{
    string str;
    int x,y;
}arr[MAXQ];
set<pair<int,int> >s;
int var[MAXN],fa[MAXN],ans[MAXQ],cnt;
int N,M,Q,tag;
int find(int x);
void merge(int x,int y);

int main()
{
    while(~scanf("%d",&N)){
        if(tag++) printf("\n");
        cnt=0;
        for(int i=0;i<N;i++){
            scanf("%d",&var[i]);
            fa[i]=i;
        }
        scanf("%d",&M);
        for(int i=0;i<M;i++){
            pair<int,int>p;
            scanf("%d%d",&p.first,&p.second);
            if(p.first>p.second) swap(p.first,p.second);
            s.insert(p);
        }
        scanf("%d",&Q);
        for(int i=1;i<=Q;i++){
            cin>>arr[i].str;
            if(arr[i].str=="query") scanf("%d",&arr[i].x);
            else{
                scanf("%d%d",&arr[i].x,&arr[i].y);
                if(arr[i].x>arr[i].y) swap(arr[i].x,arr[i].y);
                s.erase(pair<int,int>{arr[i].x,arr[i].y});
            }
        }
        
        while(!s.empty()){
            set<pair<int,int> >::iterator it=s.begin();
            merge(it->first,it->second);
            s.erase(it);
        }
        for(int i=Q;i>=1;i--){
            if(arr[i].str=="query"){
                if(var[find(arr[i].x)]>var[arr[i].x])
                    ans[++cnt]=find(arr[i].x);
                else ans[++cnt]=-1;
            } else merge(arr[i].x,arr[i].y);
        }
        for(int i=cnt;i>=1;i--) printf("%d\n",ans[i]);
    }
    return 0;
}


int find(int x)
{
    if(x==fa[x]) return x;
    else return fa[x]=find(fa[x]);
}

void merge(int x,int y)
{
    if(var[find(x)]>var[find(y)]) fa[find(y)]=find(x);
    else if(var[find(x)]<var[find(y)]) fa[find(x)]=find(y);
    else{
        if(find(x)<find(y)) fa[find(y)]=find(x);
        else fa[find(x)]=find(y);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值