PKU 1703 Find them, Catch them( 并查集的应用)

http://162.105.81.212/JudgeOnline/problem?id=1703


题意是有两个集合每次输入D a b 表示a跟b不在一个集合,输入A a b 询问a和b是否在一个集合。
以前做过这样的题目(PKU 2492),做法是用一个数组opt[t]存放t的对立,这样每次可以Union(a,opt[b]),Union(b,opt[a]),这样每次可以把相同的类合并。
        这道题还有一种做法是把这些点都合并到一个集合,可以用该点到其根结点的距离来表示与根结点的关系。这个题中可以用距离的奇偶性分类,来一组点a ,b,就把b连到a后面,这样到根结点的距离为奇数的在一个集合,为偶数的在另一个集合。思路出来了,当合并时如何更新其对于根的关系是个问题,这点纠结了很久,后面请教了liuzhe,跟他讨论了一下,才有些思路,右参考了下网上的代码,唉,BS一下自己~。
          当我们合并a,b时要把b接到a后面(这里没用按秩合并),合并时是要合并其根节点ra,rb,根据a与b的关系我们可以求出合并后rb与其根ra的关系,level[i]表示i到其根结点的距离,则level[rb]=(level[a]-level[b]+2)%2。修改了level[rb],则其依附于它的所有点的level[i](在这个集合中的关系)就要相应改变,这可以在每次查找时来更新它到依附点的关系,路径压缩后所有点都连到其根结点了,所以合并时这棵数最大高度为2,很方便更新与依附节点的关系:level[s]=(level[temp]+level[s])%2;即若其依附节点变为1,则该被依附点就要更新,否则说明其关系没有发生改变。

 

具体实现:
#include "stdio.h"

int f[100005],level[100005];
int test,n,m,ra,rb;
char c[2];
void creat_set()
{
       for(int i=1;i<=n;i++)
       {
               f[i]=i;
               level[i]=0;
       }

int find_set(int s)
{
       int temp;
       if(s!=f[s])
       {
             temp=f[s];        
             f[s]=find_set(f[s]);
             level[s]=(level[temp]+level[s])%2;  // 更新其与依附节点的关系
       }  
       return f[s];
}        

void Union(int a,int b)
{
       
      f[rb]=ra;
      level[rb]=(level[a]-level[b]+1)%2;   //修改b所在集合接到a所在集合后其根结点与新根结点的关系
      
}       
        
int main()
{
       
         int a,b;
         scanf("%d",&test);
         while(test--)
         {
              scanf("%d%d",&n,&m);
              creat_set();
              for(int i=1;i<=m;i++)
              {
                  scanf("%s%d%d",c,&a,&b);
                  ra=find_set(a);
                  rb=find_set(b);
                  if(c[0]=='D')
                     Union(a,b);
                  else
                  {
                       
                        if(ra!=rb)
                            printf("Not sure yet./n"); 
                        else if((level[a]+level[b])%2==0)
                            printf("In the same gang./n"); 
                        else
                           printf("In different gangs./n");            
                  }
              }
         }   
                 return  0;
}             
    
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值