首先树上两点(x, y)间简单路径上边权异或和,可以转化成(root, x)上路径异或和 异或 (root, y)上路径异或和,因此我们可以把每个点点权定义为从根到当前点上边权异或和,同时发现两个数异或的奇偶性只跟两个数各自二进制下1个数奇偶性有关,可以用0/1态表示。
此时问题转化为:给定一棵树,一些树边已确定了连接的两点是相同态还是相反态,然后再给出一些形如树上两点点权是相同态还是相反态的限制,求可行方案。
显然是2-sat问题
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
//#define int long long
using namespace std;
const int N = 1e6 + 100;
const ll inf = 1e18;
const ll mod = 998244353;//1e9 + 7;
#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
cerr << " " << to_string(H);
debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
vector<pii> adj2[N];
VI adj[N];
bool vis[N];
int snum = 0;
int tim, dfn[N], low[N];
bool ins[N];
VI stk;
int ocr[N];
void dfs(int x)
{
dfn[x] = low[x] = ++tim;
vis[x] = 1;
stk.pb(x);
ins[x] = 1;
trav(v, adj[x])
{
if(ins[v])
low[x] = min(low[x], dfn[v]);
if(!vis[v])
dfs(v), low[x] = min(low[x], low[v]);
}
if(dfn[x] == low[x])
{
++snum;
while(1)
{
int nw = stk.back();
stk.pop_back();
ins[nw] = 0;
ocr[nw] = snum;
if(nw == x)
break;
}
}
}
int ans[N], res[N];
void dfs2(int x, int ff)
{
trav(v, adj2[x])
{
if(v.fi == ff)
continue;
if(v.sc != -1)
res[v.fi] = res[x] ^ v.sc, dfs2(v.fi, x);
else
{
if(ans[x] == ans[v.fi])
res[v.fi] = res[x];
else
res[v.fi] = res[x] ^ 1;
dfs2(v.fi, x);
}
}
}
pii edge[N];
void sol()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n + n; i++)
adj[i].clear(), adj2[i].clear(), vis[i] = 0;
for(int i = 1; i < n; i++)
{
int x, y, z;
cin >> x >> y >> z;
adj2[x].pb(pii(y, z));
adj2[y].pb(pii(x, z));
edge[i] = pii(x, y);
if(z != -1)
{
int num = __builtin_popcount(z);
if(num & 1)
{
adj[x].pb(y + n);
adj[y + n].pb(x);
adj[x + n].pb(y);
adj[y].pb(x + n);
}
else
{
adj[x].pb(y);
adj[x + n].pb(y + n);
adj[y].pb(x);
adj[y + n].pb(x + n);
}
}
}
for(int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
int num = __builtin_popcount(z);
if(num & 1)
{
adj[x].pb(y + n);
adj[y + n].pb(x);
adj[x + n].pb(y);
adj[y].pb(x + n);
}
else
{
adj[x].pb(y);
adj[x + n].pb(y + n);
adj[y].pb(x);
adj[y + n].pb(x + n);
}
}
for(int i = 1; i <= n + n; i++)
{
if(!vis[i])
dfs(i);
}
for(int i = 1; i <= n; i++)
{
int x = i, y = i + n;
if(ocr[x] == ocr[y])
{
cout << "NO" << '\n';
return;
}
if(ocr[x] < ocr[y])
ans[i] = 0;
else
ans[i] = 1;
}
cout << "YES" << '\n';
res[1] = ans[1], dfs2(1, 0);
for(int i = 1; i < n; i++)
cout << edge[i].fi << ' ' <<edge[i].sc << ' ' << (res[edge[i].fi] ^ res[edge[i].sc]) << "\n";
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int tt;
cin >> tt;
while(tt--)
sol();
}