题目描述
给定一个无向图,边权为 0 / 1。
给定一些边的权,然后要你去把剩下的边权给定了。
要求对于图中每一个环,边权异或和为 0。
求方案数对 998'244'353 取模。
正解
先给出一个构造解的方法:将每一个点随便定一个点权,然后令边权等于两端点权异或和。
这样一定是合法的,因为一个环内每个点恰好出现了(被异或)两次(环内每一个点的度数都为 2)。
发现这样子给定点权的方案可以唯一对应一种给定边权的合法方案。
现在考虑题目中的边有什么用 —— 限制点权。
不考虑 -1 的边,因为 -1 的边对于点权并没有限制。
那么在一个联通块内,选择了一个点,那么联通块内所有的点的选择方案就确定了。
如果一个联通块有解,对答案的贡献就只有 2。
最后答案就是所有联通块答案的乘积。
upd :
但是,要发现这种方法是会算重的。。。(我的锅
一个联通块(包含 -1 边)答案还要除以 2(考虑任意一种赋点权方案,所有点权异或 1 后是同一种边权方案
感性理解下吧
代码
这么简洁的思路都有了还需要代码吗?
/* 一个联通块答案还要除以 2 */ #include <bits/stdc++.h> #define N 100005 using namespace std; const int mod = 998244353, inv2 = (mod + 1) >> 1; int n, m; int head[N], nex[N << 1], to[N << 1], eVal[N << 1], ecnt; inline int read() { int x = 0; bool neg = false; char ch = getchar(); while(!isdigit(ch)) (ch == '-') && (neg = true), ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return neg ? -x : x; } void addE(int u, int v, int w) { to[++ecnt] = v, eVal[ecnt] = w; nex[ecnt] = head[u], head[u] = ecnt; } int pVal[N], vis[N]; void dfs(int u) { vis[u] = true; for(int i = head[u], v; i; i = nex[i]) { if(eVal[i] == -1) continue; v = to[i]; if(vis[v]) { if(pVal[v] != (pVal[u] ^ eVal[i])) { // 强制无解 puts("0"); exit(0); } } else { pVal[v] = pVal[u] ^ eVal[i]; dfs(v); } } } void reDfs(int u) { vis[u] = true; for(int i = head[u], v; i; i = nex[i]) { v = to[i]; if(vis[v]) continue; reDfs(v); } } int main() { n = read(), m = read(); for(int i = 1, u, v, w; i <= m; ++i) { u = read(), v = read(), w = read(); addE(u, v, w), addE(v, u, w); } int ans = 1; for(int i = 1; i <= n; ++i) { if(vis[i]) continue; pVal[i] = 0, dfs(i); ans = 2 * ans % mod; } memset(vis, 0, sizeof vis); for(int i = 1; i <= n; ++i) { if(vis[i]) continue; reDfs(i); ans = 1LL * inv2 * ans % mod; } printf("%d\n", ans); return 0; }