Zoj 3563 Connections in Galaxy War 逆向并查集

题目链接: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;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值