ZOJ 3261 Connections in Galaxy War (离线处理+逆向并查集)

题目链接:
ZOJ 3261

题意:
给你一些点,每个点有一个权值,然后有一些边,相连且无向的。
然后给你一些操作:
query a: 表示查询 a 相连通的点中权值最高的点的编号,如果存在多个,输出编号最小的那个。
destory a , b :表示删除a,b之间的边。

题解:

逆向并查集。
如果我们直接按照常规的想法,先用并查集把所有通道连起来,然后再删除边。但是这样想,建立边的关系又把边的关系删除,好像很难实现啊。

所以,我们可以按照逆向思维,先离线输入所有的操作,然后把被destory的通道连接起来,然后再最后一个询问往前面操作,当有 destroy a,b 时就把 a,b Union_set 起来,即遇到 destroy 时,就恢复a到b之间的关系。倒着做就变成裸的并查集了。

简单来说,就是把要求删除的边全部删除,再从后往前处理操作,这样删边操作转化为了添边的操作。

AC代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
typedef long long ll;
int w,num,len;

const int N=12345;
int Power[N];
set< pair<int,int> > edge;
struct Query
{
    int op;
    int a,b;
    int ans;
    void Set_data(int opp,int aa,int bb)
    {
        op=opp;
        a=aa;
        b=bb;
    }
}query[50123];


int uset[N],mx[N];

int find_set(int x)
{
    if(uset[x]!=x)
    {
        int f=uset[x];
        uset[x]=find_set(uset[x]);
        mx[x]=max(mx[x],mx[f]);
    }
    return uset[x];
}

void union_set(int x,int y)
{
    int fx=find_set(x);
    int fy=find_set(y);
    if(fx==fy) return;
    if(mx[fy]>mx[fx]||(mx[fx]==mx[fy]&&fx>fy)) uset[fx]=fy;
    else uset[fy]=fx;

}


int main()
{
//  ios::sync_with_stdio(false);
    int n,m,q;
    int k = 0;
    while(~scanf("%d",&n))
    {
        if(k++)puts("");
        char op[10];
        int a,b;
        edge.clear();
        for(int i=0;i<n;i++) scanf("%d",&Power[i]);
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            if(a>b) swap(a,b);
            edge.insert(make_pair(a,b));
        }
        //离线处理 
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            scanf("%s",op); //I'm sb

            if(op[0]=='q')//query
            {
                scanf("%d",&a);
                query[i].Set_data(1,a,0);
            }
            else if(op[0]=='d') //destory 
            {
                scanf("%d%d",&a,&b);
                if(a>b)swap(a,b);  // WA了一次 
                edge.erase(make_pair(a,b));
                query[i].Set_data(2,a,b);
            }
        }
        for(int i=0;i<n;i++)
        {
            uset[i]=i;
            mx[i]=Power[i];
        }
        set< pair<int,int> >::iterator it;

        for(it=edge.begin();it!=edge.end();it++)
        {
            a=(*it).first;
            b=(*it).second;
            union_set(a,b);
        }
        for(int i=q-1;i>=0;i--)//倒着操作 
        {
            if(query[i].op==1) //query 
            {
                if(mx[find_set(query[i].a)] > Power[query[i].a])
                {
                    query[i].ans=uset[query[i].a]; //find the answer 
                }   
                else 
                {
                    query[i].ans= -1; //can not find the answer 
                }

            }
            else if(query[i].op==2)//destory , connect this operation
            {
                union_set(query[i].a,query[i].b);
            }
        }
        for(int i=0;i<q;i++)
        {
            if(query[i].op==1)//query 
            {
                printf("%d\n",query[i].ans);
            }               
        }

    }   
    return 0;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值