UVA -2232(并查集 + 位运算)

首先,说些关于异或的信息,异或满足交换律和结合律,这为用并查集创造了条件。

又有 a^b= x, b^c=y; a^c=x^y;

本题目有三种操作,两种存,一种查。对于建立并查集时候要维护两个信息,第一个就是当前节点和root异或的值,当前集合是否存在已知量(之需要确保根部知道这个情况就可以,一个集合里有一个一致,那么该集合任一元素都可以通过异或得到)

查的时候分为两种情况,第一种该元素所在集合没有已知值得点,那么这个集合里的元素在要查集合里有且仅有两个该集合才有解。

剩下的元素,一个个算就可以了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <algorithm>
using namespace std;
#define rep(i,n) for(int (i)=0;(i)<(n);(i)++)
const int maxn = 51000;
int p[maxn],d[maxn],hav[maxn];
int find(int x){
  if(p[x] == x) return x;
  if(hav[p[x]]==-1){
    hav[p[x]]=hav[x];
  }
  int root=find(p[x]);
  d[x]^=d[p[x]];
  return p[x]=root;
}
int a[maxn],n,m,val[maxn];
void init(){
  rep(i,n){
    p[i]=i; d[i]=0; hav[i]=-1;
  }
}
char str[maxn];
void cal(int& cnt){
  int len = strlen(str);
  rep(i,len){
    if(isdigit(str[i])){
        int j,num=0;
        for(j=i;j<len;j++){
            if(!isdigit(str[j])) break;
            num=num*10+str[j]-'0';
        }
        a[cnt++]=num;
        i=j;
    }
  }
}
int cmp(int i,int j){
  return find(i)<find(j);
}
int main ()
{
   int kase=1;
   while(scanf("%d %d",&n,&m)==2&&n){
     printf("Case %d:\n",kase++);
     init();
     gets(str);
     int ok = 1,counter=0;
     rep(gg,m){
       gets(str);
       if(!ok) continue;
       int cnt = 0;
       cal(cnt);
       if(str[0]=='I'){
          counter++;
          if(cnt == 2){
             int px=find(a[0]);
             if(hav[px]!=-1){
                find(hav[px]);
                if( (val[hav[px]]^d[hav[px]]^d[a[0]])!=a[1]){
                     ok = 0;
                }
             }
             else {
                hav[px] = a[0];
                val[a[0]] = a[1];
             }
          }
          else{
             int px=find(a[0]),py=find(a[1]);
             if(px==py){
                if((d[a[0]]^d[a[1]])!=a[2])
                    ok = 0;
             }
             else {
                d[px]=(d[a[0]]^a[2]^d[a[1]]);
                p[px]=py;
                find(px);
             }
          }
       }
       else {
          int hav_ans = 1,res=0;
          sort(a+1,a+cnt,cmp);
          for(int i=1;i<cnt;i++){
            int px=find(a[i]);
            if(hav[px]==-1){
                int ju[2]={0};
                if(i+1<cnt&&find(a[i+1])==px) ju[0]=1;
                if(i+2>=cnt||(i+2<cnt&&find(a[i+2])!=px)) ju[1]=1;
                if(ju[0]&&ju[1])
                {
                    res = (res^d[a[i]]^d[a[i+1]]);
                    i+=1;
                }
                else hav_ans = 0;
            }
            else {
                find(hav[px]);
                res=res^(val[hav[px]]^d[hav[px]]^d[a[i]]);
            }
            if(!hav_ans) break;
          }
          if(!hav_ans) printf("I don't know.\n");
          else printf("%d\n",res);
       }
       if(!ok) printf("The first %d facts are conflicting.\n",counter);
     }
     printf("\n");
   }
   return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值