题目链接:
ZOJ 3261
题意:
给你一些点,每个点有一个权值,然后有一些边,相连且无向的。
然后给你一些操作:
query a: 表示查询
a
相连通的点中权值最高的点的编号,如果存在多个,输出编号最小的那个。
destory a , b :表示删除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;
}
逆向并查集:
与并查集不同,给出一个图中原有的一些边,然后给出操作,操作不是向图中添加边,而是在已有的边上,将边删除。
对于该种情况,需要首先读入所有操作,即离线处理,把要求删除的边全部删除,再按照从后往前的顺序处理操作,这样删边操作转化为了添边的操作。