bzoj2303
并查集+数学
这道题网上好像有两种解法。
这位写的很可读:http://blog.csdn.net/unicornt_/article/details/51901225
然后看完大概就懂了做法,但是实现上还有很多细小的地方。
cnt要-1,因为第一行和第一列会算两次。
数组要开四倍。
至于为什么要拆成两个点,这是因为分开考虑01.
为什么两个flag都要赋值?因为有可能根是放在列上了,就判不到了。
还需要思考一下。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1001000; const ll mod = 1000000000; struct dsu { int fa[N * 4]; void ini(int n) { for(int i = 1; i <= n; ++i) fa[i] = i; } int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } void connect(int x, int y) { fa[find(x)] = find(y); } bool same(int x, int y) { return find(x) == find(y); } } u; int n, m, k, s = -1; int x[N], y[N], c[N]; ll ans; bool flag[N * 4]; ll power(ll x, ll t) { ll ret = 1; for(; t; t >>= 1, x = x * x % mod) if(t & 1) ret = ret * x % mod; return ret; } void solve(int s) { u.ini(2 * n + 2 * m + 100); u.connect(2, 2 + 2 * n); u.connect(3, 2 + 2 * n + 1); for(int i = 1; i <= k; ++i) { int p = 0; if(x[i] % 2 == 0 && y[i] % 2 == 0) p = 1; p = p ^ c[i] ^ s; if(p) { u.connect(x[i] * 2, y[i] * 2 + 1 + 2 * n); u.connect(x[i] * 2 + 1, y[i] * 2 + 2 * n); } else { u.connect(x[i] * 2, y[i] * 2 + 2 * n); u.connect(x[i] * 2 + 1, y[i] * 2 + 2 * n + 1); } } int cnt = 0; memset(flag, 0, sizeof(flag)); for(int i = 1; i <= n; ++i) { if(u.same(i * 2, i * 2 + 1)) return; if(!flag[u.find(i * 2)]) { flag[u.find(i * 2)] = flag[u.find(i * 2 + 1)] = 1; ++cnt; } } for(int i = 1; i <= m; ++i) { if(u.same(i * 2 + 2 * n, i * 2 + 2 * n + 1)) return; if(!flag[u.find(i * 2 + 2 * n)]) { flag[u.find(i * 2 + 2 * n)] = flag[u.find(i * 2 + 2 * n + 1)] = 1; ++cnt; } } ans = (ans + power(2ll, cnt - 1)) % mod; } int main() { scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= k; ++i) { scanf("%d%d%d", &x[i], &y[i], &c[i]); if(x[i] == 1 && y[i] == 1) s = c[i]; } if(s != 1) solve(0); if(s != 0) solve(1); printf("%lld\n", ans); return 0; }