题目大意
给定一棵 \(n(1\leq n\leq 2\times 10^5)\) 个点的树,树上有的边有边权,有的边没有。给出 \(m(1\leq m\leq 2\times 10^5)\) 个限制,每次限制了 \(u\) 到 \(v\) 路径上的边权的异或和的二进制位上 \(1\) 的个数是奇数还是偶数。现在要求给没有边权的边赋予边权,使得满足所有限制。
题解
设 \(f(x)\) 表示 \(x\) 的二进制位上 \(1\) 的个数的奇偶性,即若 \(x\) 的二进制位上有偶数个 \(1\),则 \(f(x)=0\);若 \(x\) 的二进制位上有奇数个 \(1\),则 \(f(x)=1\)。假设 \(u\) 到 \(v\) 的路径上有一系列边权 \(\{w_1,w_2,\cdots,w_k\}\),则每次限制了 \(f(w_1\bigoplus w_2 \bigoplus \cdots \bigoplus w_k)=0\) 或 \(f(w_1\bigoplus w_2 \bigoplus \cdots \bigoplus w_k)=1\)。显然有 \(f(w_1\bigoplus w_2 \bigoplus \cdots \bigoplus w_k)=f(w_1)\bigoplus f(w_2)\bigoplus \cdots \bigoplus f(w_k)\),所以我们可以把原先的边权 \(w\) 看成 \(f(w)\),需要赋予的边权要么是 \(0\) 要么是 \(1\) 即可。
设 \(s[u]\) 表示 \(u\) 到根的路径上 \(f(w)\) 的异或和,则每次限制了 \(s[u]\bigoplus s[v]=0\) 或 \(s[u]\bigoplus s[v]=1\),对于原先已有边权的边 \((u,v,w)\),则相当于限制了 \(s[u] \bigoplus s[v]=f(w)\)。于是我们可以对所有的这些限制建一张新图,当限制了 \(s[u]\bigoplus s[v]\) 的取值时,在新图的 \(u,v\) 之间直接连一条边,边权是 \(s[u]\bigoplus s[v]\),此时 \(u,v\) 两点的点权 \(s[u],s[v]\) 异或起来应该等于边权。在新图中可以划分出若干连通块,每个连通块中只需要在一开始令某个点 \(u\) 的点权 \(s[u]\) 等于 \(0\) 或 \(1\) 时,这个连通块内所有的点权就已经确定了,都可以通过异或边权转移得到。若无论点权赋成 \(0\) 或 \(1\) 都存在矛盾,则无解。于是我们计算出了一组合法的 \(s[u]\)。最终对于一开始没有边权的边 \((fa,u)\),\(fa\) 是 \(u\) 的父亲,则 \((fa,u)\) 这条边的边权就可以赋值为 \(s[u]\bigoplus s[fa]\)。
时间复杂度 \(O(n+m)\)。
Code
#include <bits/stdc++.h>
using namespace std;
template<typename elemType>
inline void Read(elemType& T) {
elemType X = 0, w = 0; char ch = 0;
while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
T = (w ? -X : X);
}
struct Graph {
struct edge { int Next, to, w; };
edge G[800010];
int head[200010];
int cnt;
Graph() :cnt(2) {}
void clear(int n) {
cnt = 2;fill(head, head + n + 2, 0);
}
void add_edge(int u, int v, int w) {
G[cnt].w = w;
G[cnt].to = v;
G[cnt].Next = head[u];
head[u] = cnt++;
}
};
Graph G, G2;
bool vis[200010];
int s[200010];
int T, n, m;
inline int f(int x) { return __builtin_parity(x); }
void Output(int u, int fa) {
for (int i = G.head[u];i;i = G.G[i].Next) {
int v = G.G[i].to;
if (v == fa) continue;
if (G.G[i].w == -1) printf("%d %d %d\n", u, v, s[u] ^ s[v]);
else printf("%d %d %d\n", u, v, G.G[i].w);
Output(v, u);
}
}
vector<int> buf;
bool DFS(int u) {
vis[u] = true;
buf.push_back(u);
for (int i = G2.head[u];i;i = G2.G[i].Next) {
int v = G2.G[i].to, w = G2.G[i].w;
if (vis[v]) {
if (s[v] ^ s[u] != w) return false;
continue;
}
s[v] = w ^ s[u];
if (!DFS(v)) return false;
}
return true;
}
int main() {
Read(T);
while (T--) {
Read(n);Read(m);
G.clear(n);
G2.clear(n);
for (int i = 1;i <= n - 1;++i) {
int u, v, w;
Read(u); Read(v); Read(w);
G.add_edge(u, v, w);
G.add_edge(v, u, w);
if (w != -1) {
w = f(w);
G2.add_edge(u, v, w);
G2.add_edge(v, u, w);
}
}
fill(vis + 1, vis + n + 1, false);
bool flag = true;
for (int i = 1;i <= m;++i) {
int u, v, w;
Read(u); Read(v); Read(w);
if (u == v) {
if (w) flag = false;
continue;
}
G2.add_edge(u, v, w);
G2.add_edge(v, u, w);
}
if (!flag) { printf("NO\n"); continue; }
for (int u = 1;u <= n;++u) {
if (vis[u]) continue;
s[u] = 0; buf.clear();
if (!DFS(u)) {
for (auto x : buf) vis[x] = false;
buf.clear(); s[u] = 1;
if (!DFS(u)) { flag = false; break; }
}
}
if (!flag) {
printf("NO\n");
continue;
}
printf("YES\n");
Output(1, 0);
}
return 0;
};