http://acm.hdu.edu.cn/showproblem.php?pid=3234
#Description
给你N个数,X0…X(N-1)
执行Q个查询
三种格式
I p v
Xp= v
I p q v
Xp XOR Xq = v
Q k p1 p2 … pk
回答Xp1 XOR Xp2 XOR … XOR Xpk 是多少?
对于每个Q
如果之前提供的信息不能算出来,输出
“I don’t know.”
如果第i个 I 与之前的 I 有矛盾,输出
“The first i facts are conflicting.”
然后后面的内容全部不管
否则就输出答案
#Algorithm
##XOR的性质
- X XOR 0 = X 任何数XOR自己为他们本身
- X XOR X = 1 任何数XOR自己 = 1
- A XOR B XOR C = A XOR C XOR B XOR满足交换律
带权并查集
权值v[i] = a[i] XOR a[id[i]] // id[i]就是代码中的par[i]
然后还需要设置超级根 a[N] = 0
由XOR性质1可得
这样 I p v 就是 I p n v
二者都执行 unite(p, q, w) //代码中的 void unite(int x, int y, int w)
如果p q的root都一样
由XOR性质2,3可得
p xor q = p xor root xor q xor root = v[x] XOR v[y]
这样就可以判断是否矛盾
我的代码里就没有使用加权的并查集,因为显然超级根N最适合做根
这样默认j做i的父亲//代码中卫fy 做 fx的父亲
新的v[i] = x[i] xor x[j]
因为 v[p] = x[p] xor x[i] v[q] = x[q] xor x[j] w = x[p] xor x[q]
所以 v[i] = v[p] xor w xor v[q]
find时
v[p] 原本是 x[p] xor x[id[p]]
但由于路径压缩
新的v[p] = x[root] xor x[p]
v[id[p]] = x[root] xor x[id[p]]
所以
v[p]只需要 ^= v[id[p]]就好了
Q的时候
用个ans把全部v[i] XOR
因为v[i] = x[i] XOR x[id[i]]
如果id[i]出现了偶数次,就消掉了
当然了,n可以不管多少次
因为如果 id[i] = n 那么 v[i] = x[i]
这样出现奇数次的id并且不是n,那么就不知道了
#Code
//约定 a[i] 表示 a[i]的值
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
const int MAXN = 20000 + 9;
int par[MAXN];
int v[MAXN]; //当前值是a[i] v[i] = a[i] xor a[par[i]]
bool bug, know;
struct UnionFind {
int n;
UnionFind(){};
void init(int nn) {
n = nn;
//memset(v, 0, sizeof(v));
for (int i = 0; i <= n; i++) {
par[i] = i;
v[i] = 0;
}
}
int find(int x) {
if (par[x] != x) {
int fx = find(par[x]);
v[x] ^= v[par[x]];
/**
* v[x] = a[x] xor a[par[x]]
* v[par[x]] = a[par[x]] xor a[par[par[x]]]
*/
par[x] = fx;
}
return par[x];
}
void unite(int x, int y, int w) { //a[x] xor a[y] = w
int fx = find(x);
int fy = find(y);
if (fx == fy) {
if ((v[x]^v[y]) != w) bug = true;
/**
* v[x] = a[x] xor a[fx]
* v[y] = a[y] xor a[fy]
* fx == fy a[fx] = f[fy]
* v[x] xor v[y] = a[x] xor a[y] = w
*/
return;
}
if (fx == n) swap(fx, fy);
par[fx] = fy;
v[fx] = v[x] ^ w ^ v[y];
/**
* 此处没用启发式合并
* v[x] = a[x] xor a[fx]
* v[y] = a[y] xor a[fy]
* 现在par[x] 变成 fy
* v[fx] = a[fx] xor a[par[x]] = a[fx] xor a[fy]
* w = a[x] xor a[y]
* v[fx] = v[x] xor w xor v[y]
*/
}
bool same(int x, int y) {
return find(x) == find(y);
}
void print() {
for (int i = 0; i < n; i++) {
printf("%d ", par[i]);
}
cout << endl;
}
int total() {
int s = 0;
for (int i = 0; i < n; i++) {
if (par[i] == i) s++;
}
return s;
}
};
int n, q;
char s[1000]; //读入字符串
int Itotal;
UnionFind unionFind;
void solveIpv(int p, int v) {
// cout << "p=" << p << "v=" << v << endl;
unionFind.unite(p, n, v);
}
void solveIpqv(int p, int q, int v) {
//cout << "p=" << p << "q=" << q << "v=" << v << endl;
unionFind.unite(p, q, v);
}
void solveI() {
Itotal++;
gets(s);
if (bug) return;
int a, b, c;
if (sscanf(s, "%d%d%d",&a, &b, &c) == 2) {
solveIpv(a, b);
}else {
solveIpqv(a, b, c);
}
if (bug) {
printf("The first %d facts are conflicting.\n", Itotal);
}
}
void solveQ() {
//unionFind.print();
know = true; //是否输出i don't know
int k;
scanf("%d", &k);
//cout << "k=" << k << endl;
int ans = 0;
map<int, int> map1; //用来存父亲出现的次数
for (int i = 0; i < k; i++) {
int x;
scanf("%d", &x);
//cout << "x=" << x << endl;
int fx = unionFind.find(x);
// cout << "x = " << x << "fx = " << fx << endl;
ans ^= v[x];
map1[fx]++;
}
if (bug) return;
for (map<int, int>::iterator it = map1.begin(); it != map1.end(); it++) {
if ((it -> second) % 2) { //相同父亲节点是奇数个 xor 消不了
if (it->first != n) {
// cout << "*" << it->first << endl;
know = false;
break;
}
}
}
if (!know) {
puts("I don't know.");
} else {
printf("%d\n", ans);
}
}
void solve() {
Itotal = 0; //ca2
bug = false;
unionFind.init(n);
for (int i = 0; i < q; i++) {
//unionFind.print();
char s[20];
scanf("%s", s);
// cout << "ch=" << ch << endl;
if (s[0] == 'I') {
solveI();
}else {
solveQ();
}
}
}
int main() {
//freopen("in.txt", "r", stdin);
for (int i = 1;;i++) {
scanf("%d%d\n", &n, &q);
if (n == 0) break;
printf("Case %d:\n", i);
solve();
printf("\n");
}
}
#第一次写的时候
我的内心是崩溃的
Debug了一个小时,结果,少了一个括号
((v[x]^v[y]) != w)
(v[x]^v[y] != w)
这两个表达式的结果是不一样了 = =
位运算的优先级低我是知道的,没有想到这么低。还能过Sample,这就让人很崩溃了。
以后凡是有位运算的,一律打括号!
这题很有意义,要好好写题解,不过先游个泳,冷静一下。