解题思路
,这道题目看题解才看出来的,先看边权 其实不好操作,那么我们可以调整一下,一条边连接2个点,把边权,改成它连接2点的异或值,一个环的异或值需要为0,对于环中的点,开头的点任意取1,或者0,对于整个环的异或值 =0恒成立。
所以对于连通块,首先对于给定0,1值的边,保存起来,通过我们转换点值的定义,去先把这些连通值处理,如果存在冲突,即无解,直接输出0,程序结束。
否则只剩下边权为-1的边,值确定的边可以添加到连通块。
情况1、这条边在一个连通块中充当环中最后一条边,那么这条边就可以通过前面的值以及边权异或值为0,唯一确定。
情况2、这条边是连通另外一个连通块,可能只有一个元素或者一个多个元素,这样的化这条边可取0/1两个值,对应后面的情况为知边,直接跟着取反,保证异或值仍为0。
连通块通过并查集处理,中途退出 函数,这样一条条边去把连通块连接起来,最终变成一个连通块,方案数就是答案。注意开到 ,说的可能不是很清楚,图比较好理解。
#include <bits/stdc++.h> using namespace std; #define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0) typedef long long ll; inline int read() { int s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } const int N = 1e5 + 7; const int MOD = 998244353; struct Node { int v; int cost; }; vector<Node> e[N]; int u[N], v[N], w[N]; int father[N], fatheri; int val[N]; int n, m; int find(int root) { int son = root; while (root != father[root]) root = father[root]; while (son != root) { int temp = father[son]; father[son] = root; son = temp; } return root; } void merge(int a, int b) { int fa = find(a); int fb = find(b); if (fa != fb) { father[fa] = fb; } } void dfs(int x) { father[x] = fatheri; for (auto it : e[x]) { int v = it.v, w = it.cost; if (father[v] && (val[v] ^ val[x]) != w) { //存在和我们定义填涂冲突节点 puts("0"); exit(0); // 让程序正常运行结束 } if (father[v]) continue; val[v] = val[x] ^ w; dfs(v); } } int main() { n = read(), m = read(); for (int i = 1; i <= m; ++i) { u[i] = read(), v[i] = read(), w[i] = read(); if (~w[i]) { // w[i]!=-1,存在已知边权值,方案已经固定为1 e[u[i]].push_back(Node{ v[i],w[i] }); e[v[i]].push_back(Node{ u[i],w[i] }); } } for (int i = 1; i <= n; ++i) { if (!father[i]) { fatheri = i; // 把环内元素父亲都定义为i dfs(i); // 对环进行赋值 } } ll ans = 1; //取模不开ll,容易流泪 +_+ for (int i = 1; i <= m; ++i) { if (father[find(u[i])] != father[find(v[i])]) { //不在同一个环中,存在2种填涂方案 ans = (ans << 1) % MOD; merge(u[i], v[i]); } } printf("%lld\n", ans); return 0; }