与并查集不同,给出一个图中原有的一些边,然后给出操作,操作不是向图中添加边,而是在已有的边上,将边删除。对于该种情况,需要把首先读入所有操作,把要求删除的边全部删除,再按照从后往前的顺序处理操作,这样删边操作又重新转化为了添边的操作。
例题:
题目大意:有n个卫星,每个卫星有一个power值,初始时这些卫星之间有若干条边,有两种操作一种是删边,另一种是查询,查询卫星a,即要求找出与卫星a直接或间接相连的卫星中power值大于该卫星的拥有最大power值的卫星,若两卫星power值相同且最大输出编号小的那个。
例题:
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]);
}
}