异或性质+带权并查集维护
主要是异或性质, 题目要求每个(2ast2)的矩形内异或值为1
在使用(2ast2)的矩形异或的时候,考虑对于一个矩形,除了最外面一圈,内部的格子都会被异或四次,除了内部和四个顶点的格子会被异或两次,所以可以不考虑(同一个值异或两次等于不异或),只用考虑四个顶点,那么考虑矩形 ((a,b)(a,c)(b,d)(c,d))
我们可以得到
在矩形长宽均为偶数的情况 (1 = val_{a,b} igoplus val_{a,c} igoplus val_{b,d} igoplus val_{c,d})
在矩形长宽不均为偶数的情况 (0 = val_{a,b} igoplus val_{a,c} igoplus val_{b,d} igoplus val_{c,d})
更进一步,对于已知两个顶点的矩形,我们可以知道剩下两个点是否相同。
那么我们令 (a = 1, b = 1) 来做此题。
然后枚举 (val_{1, 1}) 的值,就行了。
取0和取1是相反的状态,直接给K个确定点取反就行了。
注意:
if ((!(x[i] & 1)) && (!(y[i] & 1))) {
k[i] ^= 1;
}
以上代码是为了统一评判标准。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll MAXN = 1e6+10;
const ll MOD = 1e9;
ll N, M, K, x[MAXN], y[MAXN], k[MAXN], g[MAXN], fa[MAXN];
ll calc(ll);
ll find_(ll);
ll kuaisu(ll, ll);
int main() {
scanf("%lld%lld%lld", &N, &M, &K);
ll flag = -1;
for (ll i = 1; i <= K; i++) {
scanf("%lld%lld%lld", x+i, y+i, k+i);
if ((!(x[i] & 1)) && (!(y[i] & 1))) {
k[i] ^= 1;
}
if (x[i] == 1 && y[i] == 1) {
flag = k[i];
}
}
if (-1 != flag) {
printf("%lld
", calc(flag));
} else {
ll ans = calc(0);
printf("%lld
", (ans + calc(1)) % MOD);
}
return 0;
}
ll calc(ll opt) {
if (opt == 1) {
for (ll i = 1; i <= K; i++) if (x[i] > 1 && y[i] > 1) {
k[i] ^= 1;
}
}
for (ll i = 1; i <= N + M; i++) fa[i] = i, g[i] = 0;
fa[N+1] = 1;
for (ll i = 1; i <= K; i++) {
ll a = x[i], b = y[i], c = k[i];
if (a != 1 || b != 1) {
ll fx = find_(a), fy = find_(b+N);
ll tp = g[a] ^ g[b + N] ^ c;
if (fx != fy) {
fa[fy] = fx;
g[fy] = tp;
} else if (tp) {
return 0;
}
}
}
ll tot = 0;
for (ll i = 1; i <= N + M; i++) {
if (i == find_(i)) tot++;
}
return kuaisu(2, tot-1) % MOD;
}
ll kuaisu(ll n, ll tim) {
ll ret = 1;
while (tim) {
if (tim & 1) {
ret = ret * n % MOD;
}
tim >>= 1;
n = n * n % MOD;
}
return ret;
}
ll find_(ll x) {
ll q;
if (fa[x] == x) q = x;
else {
q = find_(fa[x]);
g[x] ^= g[fa[x]];
fa[x] = q;
}
return q;
}