WuHan 2009 / UVa 12232 / HDU 3234 Exclusive-OR (异或的性质&加权并查集&合并时保持根结点不变)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=3384

http://acm.hdu.edu.cn/showproblem.php?pid=3234


1. 对于第一种命令I p v,我们可以虚拟出一个点Xn = 0,那么p^Xn = v,故第一和第二种命令我们可以统一成p^q = v的模式。

2. 对于第三种命令,记val[i] = Xi^Xfa[i],那么Xp1^Xp2^...^Xpk = (val[Xp1]^...^val[Xpk]) ^ (Xfa[Xp1]^...^Xfa[Xpk])

val[Xp1]^...^val[Xpk]易求,对于Xfa[Xp1]^...^Xfa[Xpk],由于a^c^b^c = a^b,可先判断Xfa[Xpi]出现的次数的奇偶性,偶数不用管,奇数看Xfa[Xpi]是否为Xn,若不是则在Xp1^Xp2^...^Xpk中必有未知信息,若是则异或Xfa[Xpi]即可。


完整代码:

/*0.102s*/

#include<bits/stdc++.h>
using namespace std;
const int mx = 20005;

int val[mx], fa[mx], n;
char str[100];

int find(int x)
{
	if (~fa[x])
	{
		int tmp = fa[x];
		fa[x] = find(fa[x]);
		val[x] ^= val[tmp]; ///pushdown
		return fa[x];
	}
	return x;
}

bool merge(int x, int y, int v)
{
	int fx = find(x), fy = find(y);
	if (fx == fy) return (val[x] ^ val[y]) == v;
	if (fx == n) swap(fx , fy); ///技巧:始终使得Xn(虚拟的点)为根结点
	fa[fx] = fy, val[fx] = val[x] ^ v ^ val[y];
	return true;
}

int main()
{
	//freopen("in.txt", "r", stdin);
	int m, p, q, v, k, x, ans, cas = 0, facts;
	bool isError;
	map<int, int> mp;
	map<int, int>::iterator iter;
	while (scanf("%d%d", &n, &m), n)
	{
		getchar();
		printf("Case %d:\n" , ++cas);
		memset(val, 0, sizeof(val));
		memset(fa, -1, sizeof(fa));
		facts = 0;
		isError = false;
		while (m--)
		{
			if (getchar() == 'I')
			{
				gets(str);
				++facts;
				if (isError) continue;
				if (sscanf(str, "%d%d%d", &p, &q, &v) == 2) v = q, q = n; ///换成根结点
				if (!merge(p, q, v))
				{
					isError = true;
					printf("The first %d facts are conflicting.\n" , facts++);
				}
			}
			else
			{
				scanf("%d", &k);
				ans = 0;
				mp.clear();
				while (k--)
				{
					scanf("%d", &x);
					if (isError) continue;
					find(x); ///更新一下值再计算
					ans ^= val[x];
					++mp[find(x)];
				}
				getchar();
				if (isError) continue;
				for (iter = mp.begin(); iter != mp.end(); ++iter)
				{
					if (iter->second & 1)
					{
						if (iter->first != n) break; ///存在未知信息
						else ans ^= val[iter->first];
					}
				}
				if (iter == mp.end()) printf("%d\n" , ans);
				else puts("I don't know.");
			}
		}
		putchar(10);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值