逆向并查集(ZOJ 3261)

与并查集不同,给出一个图中原有的一些边,然后给出操作,操作不是向图中添加边,而是在已有的边上,将边删除。对于该种情况,需要把首先读入所有操作,把要求删除的边全部删除,再按照从后往前的顺序处理操作,这样删边操作又重新转化为了添边的操作。


例题:


ZOJ3261 Connections in Galaxy War

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3261


题目大意:有n个卫星,每个卫星有一个power值,初始时这些卫星之间有若干条边,有两种操作一种是删边,另一种是查询,查询卫星a,即要求找出与卫星a直接或间接相连的卫星中power值大于该卫星的拥有最大power值的卫星,若两卫星power值相同且最大输出编号小的那个。

分析:按上述方法,先删边,再反向处理操作,注意该题优先级的描述,再Union中要分类处理。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <set>
using namespace std;
#define MAXN 10010
#define MAXM 50010

int query[MAXM][2];
int par[MAXN],power[MAXN];
int ans[MAXM];
set<int> g[MAXN];

void init(int n)
{
    for(int i=0;i<n;i++)
    {
        par[i]=i;
        g[i].clear();
        scanf("%d",&power[i]);
    }
}

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

void Union(int a,int b)
{
    
    int pa=find(a);
    int pb=find(b);
    if(power[pa]<power[pb])    par[pa]=pb;
    else if(power[pa]>power[pb])    par[pb]=pa;
    else
    {
        if(pa<pb)    par[pb]=pa;
        else par[pa]=pb;
    }

}

int main()
{
    int n,m,q,b=0;
    while(scanf("%d",&n)!=EOF)
    {
        if(b)    printf("\n");
        b=1;
        init(n);
        scanf("%d",&m);
        int a,b;
        char str[20];
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            if(a>b)    swap(a,b);
            g[a].insert(b);
        }
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            scanf("%s",str);
            if(str[0]=='d')
            {
                scanf("%d%d",&a,&b);
                if(a>b)    swap(a,b);
                query[i][0]=a;    query[i][1]=b;
                g[a].erase(g[a].find(b));
            }
            else
            {
                scanf("%d",&a);
                query[i][0]=a;    query[i][1]=-1;
            }
        }
        for(int i=0;i<n;i++)
        {
            for(set<int>::iterator it=g[i].begin();it!=g[i].end();it++)
                Union(i,*it);
        }
        int cnt=0;
        for(int i=q-1;i>=0;i--)
        {
            if(query[i][1]==-1)
            {
                int tmp=find(query[i][0]);
                if(power[tmp]==power[query[i][0]])    ans[cnt++]=-1;
                else    ans[cnt++]=tmp;
            }
            else    Union(query[i][0],query[i][1]);
        }
        for(int i=cnt-1;i>=0;i--)
        printf("%d\n",ans[i]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值