ZOJ 3261 Connections in Galaxy War (并查集)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3261


题意:N个星球,编号从0到N-1。每个星球有一个战斗力power,且这N个星球之间建有一些通道,可以相互联系,在星球大战中,一些星球的power不够强,要向和自己联通的星球中power最强的星球求救,且在星球大战中会有一些通道被损坏。然后有一些问题:destroy a b :a和b之间的通道被损坏。 query a:问a星球该向谁求救。


分析:并查集。如果一开始将星球大战之前建立的通道Union起来,以最大power的节点作为根节点,并按顺序读入数据,当遇到 query a时,就直接输出,但遇到destroy a b时,就不能简单的删掉a和b之间的边了,那么根节点的最大power值就无法维护。但如果我们先输入所有数据,即所谓的离线输入,将星球大战之后的状态作为初始状态,进行并查集的操作,遇到destroy时,就恢复a到b之间的通道,那么就很容易维护根节点的性质了,那么这题就变得很简单了。


Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <map>
#include <set>
#define eps 1e-8
#define LL long long
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;

const int inf=0x3f3f3f3f;
const int maxn=10005;
const int HASH=10000;
int f[maxn],power[maxn],ans[maxn*5];
struct node{
    int u,v;
}tun[maxn<<1];
struct Node{
    int a,b;
    char cmd[10];
}query[maxn*5];
map<int,bool>mp;
int n,m,Q;

void make_set(){
    for(int i=0;i<n;i++) f[i]=i;
}

int find_set(int x){
    if(x!=f[x]){
        f[x]=find_set(f[x]);
    }
    return f[x];
}

void Union(int x,int y){
    int fx=find_set(x);
    int fy=find_set(y);
    if(fx!=fy){
        if(power[fx]>power[fy]) f[fy]=fx;
        else if(power[fx]<power[fy]) f[fx]=fy;
        else {
            if(fx<fy) f[fy]=fx;
            else f[fx]=fy;
        }
    }
}

int main()
{
    bool first=true;
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++) scanf("%d",&power[i]);
        scanf("%d",&m);
        for(int i=0;i<m;i++){
            scanf("%d %d",&tun[i].u,&tun[i].v);
            if(tun[i].u>tun[i].v) swap(tun[i].u,tun[i].v);
        }
        mp.clear();
        scanf("%d",&Q);
        for(int i=0;i<Q;i++){
            scanf("%s",query[i].cmd);
            if(query[i].cmd[0]=='q') scanf("%d",&query[i].a);
            else {
                scanf("%d %d",&query[i].a,&query[i].b);
                if(query[i].a>query[i].b) swap(query[i].a,query[i].b);
                mp[query[i].a*HASH+query[i].b]=true;
            }
        }
        make_set();
        for(int i=0;i<m;i++){
            if(!mp[tun[i].u*HASH+tun[i].v]) Union(tun[i].u,tun[i].v);
        }
        int cnt=0;
        for(int i=Q-1;i>=0;i--){
            if(query[i].cmd[0]=='q') {
                int fa=find_set(query[i].a);
                if(power[fa]>power[query[i].a]) ans[cnt++]=fa;
                else ans[cnt++]=-1;
            }
            else Union(query[i].a,query[i].b);
        }
        if(first) first=false;
        else printf("\n");
        for(int i=cnt-1;i>=0;i--)
            printf("%d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值