您的位置:首页 > 其它

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

2017-06-14 20:52 375 查看
题目链接:

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
;
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
,mx
;

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;
}


逆向并查集:

  与并查集不同,给出一个图中原有的一些边,然后给出操作,操作不是向图中添加边,而是在已有的边上,将边删除。

  对于该种情况,需要首先读入所有操作,即离线处理,把要求删除的边全部删除,再按照从后往前的顺序处理操作,这样删边操作转化为了添边的操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: