有 n (≤20000) 個未知整數 X₀…Xₙ₋₁,支援兩類操作(Q ≤ 40000):
若新增事實與已知衝突,輸出衝突信息;查詢無法確定時輸出 "I don't know."。
引入虛擬節點 `n` 代表常數 0。每個節點 `x[i]` 存儲「節點 i 到其根節點的異或值」。
int pa[maxn]; // 父節點
int val[maxn]; // 到父節點的異或值 (路徑壓縮後為到根的異或值)
int cnt[maxn]; // 查詢時統計各集合出現次數
int n; // 變量數,虛擬節點索引為 n
void init() {
for (int i = 0; i <= n; ++i) {
pa[i] = i;
val[i] = 0;
}
}
int find(int x) {
if (x != pa[x]) {
int root = find(pa[x]);
val[x] ^= val[pa[x]]; // 累積到根的異或值
return pa[x] = root;
}
return x;
}
bool unite(int p, int q, int v) {
int rp = find(p), rq = find(q);
if (rp == rq) return (val[p] ^ val[q]) == v;
// 將 rp 掛到 rq 下
pa[rp] = rq;
val[rp] = val[p] ^ val[q] ^ v; // 關鍵公式推導
return true;
}
// 統計每個集合出現次數
for (int i = 0; i < k; ++i) cnt[find(id[i])] = 0;
for (int i = 0; i < k; ++i) {
cnt[find(id[i])]++;
ans ^= val[id[i]];
}
// 判斷是否可確定:每個集合出現次數必為偶數,或包含虛擬節點 n
bool ok = true;
for (int i = 0; i < k; ++i) {
int r = find(id[i]);
if (cnt[r] % 2 == 1 && r != n) { ok = false; break; }
}
此模板可直接應用於: