HDU 3234 Exclusive-OR 并查集变形

/*
	转自:http://www.cppblog.com/Yuan/archive/2010/09/02/125667.html?opt=admin稍许改写
    有n(n<=20000)个未知的整数X0,X1,X2Xn-1,有以下Q个(Q<=40000)操作: 
    I p v :告诉你Xp=v 
    I p q v :告诉你Xp Xor Xq=v 
    Q k p1 p2 … pk : 询问 Xp1 Xor Xp2 .. Xor Xpk, k不大于15。 
    如果当前的I跟之前的有冲突的话,跳出    

    并查集扩展!
    对于I p v ,如果虚设一个点Xn=0,则可以看成 I p n v  (与0异或)
    所以对于所有那些I,都是a^b=v,两个两个的,连带的效果
    所以设val[i]=Xi^Xfa[i]  跟上面一样有连带效果  fa[i]为i的父亲
    这样:
    1) I p q v
    先find
    如果p q在同一集合,判断是否有Xp^Xq=v  不是的话矛盾
    否则,合并  注意虚设的点n要始终保持为根
    2)Q k p1pk
    Xp1 ^ Xp2  ^ Xpk
    转化为:
    val[p1] ^ val[p2]   ^ val[k]   ^   (Xfa[p1] ^ Xfa[p2]  ^ Xfa[pk])
    val[pi]已知,只需判断Xfa[pi]是否已知
    由于是异或,奇数个Xfa[pi]才需判断。判断方法为看他的根是不是Xn即可
        
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 20010;
int fa[MAXN],val[MAXN];
int n,q;
void init(int n)
{
    for(int i=0;i<=n;i++)
    {
        val[i]=0;
        fa[i]=i;
    }
}
int find(int a)
{
    if(a!=fa[a])
    {
        int t=fa[a];
        fa[a]=find(fa[a]);
        val[a]^=val[t];
    }
    return fa[a];
}

bool unin(int a,int b,int v)
{
    int ra=find(a);
    int rb=find(b);
    if(ra==rb)
    {
        return v==(val[a]^val[b]);
    }
    if(ra==n)swap(ra,rb);
    fa[ra]=rb;
    val[ra]=val[a]^val[b]^v;
    return true;
}

char cmd[MAXN];

int main()
{
    //freopen("in","r",stdin);
    int t=1;
    while(scanf("%d%d",&n,&q),n)
    {
        printf("Case %d:\n",t++);
        init(n);
        int fact = 0;
        bool err = false;
        for(int i=0;i<q;i++)
        {
            scanf(" %c",&cmd[0]);
            if(err){gets(cmd);continue;}
            if(cmd[0]=='I')
            {
                gets(cmd);//需要这样子
                fact++;
                int x,y,v;
                if(sscanf(cmd,"%d%d%d",&x,&y,&v)==2)//sscanf()   直接scanf()会错不知为啥
                {
                    swap(y,v);
                    y=n;
                }
                if(!unin(x,y,v))
                {
                    err = true;
                    printf("The first %d facts are conflicting.\n",fact);
                }
            }
            else
            {
                int k,x,ans=0,pos=-1,flag=true;
				vector<pair<int,int> > V;
                scanf("%d",&k);
				for(int i=0,jj;i<k;i++)
				{
					scanf("%d",&x);
					int rx=find(x);
					rx=find(x);
					ans^=val[x];
					for(jj=0;jj<V.size();jj++)
					{
						if(V[jj].first==rx)break;
					}
					if(jj==V.size())V.push_back(make_pair(rx,1));
					else V[jj].second++;
				}
				for(int j=0;j<V.size();j++)
				{
					if(V[j].second&1)
					{
						if(V[j].first!=n){flag=false;break;}
					}
				}
				  if(flag)printf("%d\n",ans);
                else puts("I don't know.");
			}
        }
        puts("");
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值