https://zoj.pintia.cn/problem-sets/91827364500/problems/91827368062
题目大意:给出
n
n
n个星球的能量值
p
i
p_{i}
pi,
m
m
m条边描述了这些星球之间的关系,现在有
q
q
q个询问,
d
e
s
t
o
r
y
a
b
destory \quad a \quad b
destoryab,表示消去
a
a
a、
b
b
b之间的边,
q
u
e
r
y
a
query \quad a
querya,表示查询
a
a
a所能求救的星球编号。
a
a
a只能向与自己相连(直接或间接均可)且能量值大于自己的星球求救,如果有多个选择,它会选择编号最小的那个星球。
思路:并查集。但是肯定不能正着做,因为并查集是不支持删除操作的。我们考虑把询问离线下来,从后向前考虑,这样就把删边操作变成了加边操作,用并查集就可以实现了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define pr pair<int,int>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
map<pr,bool> my;
int n,m,q;
int f[maxn],p[maxn];
int x[maxn<<1],y[maxn<<1];
int ans[maxn*5];
char s[10];
struct qr
{
bool tag;
int a,b;
}query[maxn*5];
void init()
{
for(int i=0;i<n;i++)
f[i]=i;
}
int father(int u)
{
return f[u]==u?u:f[u]=father(f[u]);
}
void Union(int u,int v)
{
int fu=father(u);
int fv=father(v);
if(p[fu]>p[fv])
f[fv]=fu;
else if(p[fu]<p[fv])
f[fu]=fv;
else
{
if(fu>fv)
f[fu]=fv;
else
f[fv]=fu;
}
}
int main()
{
int times=0;
while(~scanf("%d",&n))
{
init();
my.clear();
for(int i=0;i<n;i++)
scanf("%d",&p[i]);
scanf("%d",&m);
for(int i=0;i<m;i++)
scanf("%d %d",&x[i],&y[i]);
scanf("%d",&q);
int t1,t2;
for(int i=0;i<q;i++)
{
scanf("%s",s);
if(s[0]=='q')
{
scanf("%d",&query[i].a);
query[i].tag=0;
}
else
{
scanf("%d %d",&query[i].a,&query[i].b);
if(query[i].a>query[i].b)
swap(query[i].a,query[i].b);
my[pr(query[i].a,query[i].b)]=1;
query[i].tag=1;
}
}
for(int i=0;i<m;i++)
{
if(x[i]>y[i])
swap(x[i],y[i]);
if(!my.count(pr(x[i],y[i])))
Union(x[i],y[i]);
}
int cnt=0;
for(int i=q-1;i>=0;i--)
{
if(query[i].tag)
Union(query[i].a,query[i].b);
else
{
int tmp=father(query[i].a);
if(p[tmp]>p[query[i].a])
ans[cnt++]=tmp;
else
ans[cnt++]=-1;
}
}
if(times++)
printf("\n");
for(int i=cnt-1;i>=0;i--)
printf("%d\n",ans[i]);
}
return 0;
}