题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3563
题目大意:先给你N个城市的能力, 然后给你M条路,再然后又Q次询问,如果为 query,输入 x,则询问与当前城市x相连的且比当前城市能力大的城市中能力最大的城市的编号,如果有多个最大城市,输出编号最小的,否则输出-1.如果为destory,输入x,y,则切断x,y这条路。
解题思路:如果正向查找的话,当切断一条路时没办法确定他们之间的关系了,因为查找时为路径压缩;
但是我们可以把询问存起来,把切断的路径标记一下,没标记的路径进行合并,注意合并的时候把能力大的城市当做根,如果能力一样,位置小的当做根,倒着遍历,如果遇见切断路径,就把它加进路径里面,接着就是并查集了,代码如下:
#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
int f[10005],ans[50005];//f存父节点,ans存最后答案
int n,m,q;
map<int,bool>mp;//标记一条路是否被破坏
struct node{int v,id;}rank[10005];//每个星球的能力和位置
struct link{int x;int y;}s1[20005];//存x,y之间有路连接
struct que{char c[10];int x,y;}s2[50005];//存查询记录
int find(int x)
{
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
//合并先按能力合并,能力相同按照位置排合并
void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
if(rank[fx].v>rank[fy].v) f[fy]=fx;
else if(rank[fx].v<rank[fy].v) f[fx]=fy;
else
{
if(rank[fx].id>rank[fy].id) f[fx]=fy;
else f[fy]=fx;
}
}
}
int main()
{
bool flog=false;
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)//输入初始化
{
scanf("%d",&rank[i].v);
rank[i].id=i;
f[i]=i;
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&s1[i].x,&s1[i].y);
if(s1[i].x>s1[i].y) swap(s1[i].x,s1[i].y);//交换位置以保证两条路之间有唯一标记
}
scanf("%d",&q);
mp.clear();//清零
//存查询结果
for(int i=0;i<q;i++)
{
scanf("%s",s2[i].c);
if(s2[i].c[0]=='d')
{
scanf("%d%d",&s2[i].x,&s2[i].y);
if(s2[i].x>s2[i].y) swap(s2[i].x,s2[i].y);
mp[s2[i].x*10000+s2[i].y]=true;//标记被毁坏的
}
else
scanf("%d",&s2[i].x);
}
//把没毁坏的路先并起来
for(int i=0;i<m;i++)
if(!mp[s1[i].x*10000+s1[i].y])
merge(s1[i].x,s1[i].y);
int cnt=0;
//逆向查找
for(int i=q-1;i>=0;i--)
{
if(s2[i].c[0]=='q')
{
int k=find(s2[i].x);
if(rank[k].v>rank[s2[i].x].v)
ans[cnt++]=rank[k].id;
else
ans[cnt++]=-1;
}
else
//如果路被毁坏,则毁坏之前肯定联通,就加进去
merge(s2[i].x,s2[i].y);
}
if(flog) puts("");
else flog=true;
for(int i=cnt-1;i>=0;i--)
printf("%d\n",ans[i]);
}
return 0;
}