Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Root the tree. For a subtree, do not force the component containing its root to be valid immediately; treat it as an open component that can still merge upward.
Once every already separated component inside a subtree is valid, the open component's XOR is forced: it is the subtree XOR the XOR of all completed component values.
Completed components only have XORs from . Since , store a -bit parity mask: bit says whether an odd number of completed components with XOR have appeared.
When combining a child, the edge to it has two choices. Keep it: masks just XOR together. Cut it: legal only if the child's open XOR equals some , and then you additionally toggle bit .
At the root, the last open component has nowhere to merge. Sum all masks where belongs to the set .
Root the tree at vertex . The main trap is trying to DP over every possible XOR of the component touching the current vertex. Those XORs are 30-bit values, so that plan blows up immediately. The useful observation is that we do not need to store that XOR directly.
For a vertex , let be the XOR of all in the subtree of . During a rooted-tree DP, there is always one special component in this subtree: the component containing . Call it the open component, because it may later merge with 's parent. Every other component inside the subtree must already be valid.
Since every completed component has XOR equal to one of , and , we can store only parities. For a mask , define
Now define as the number of ways to cut edges completely inside the subtree of such that:
Then the XOR of the open component is not extra state. It is forced:
Why? The XOR of the whole subtree equals the XOR of all completed components plus the XOR of the open component. The completed components contribute exactly , because equal values cancel in pairs under XOR. So the open component must be . Nice little bitmask robbery.
For a leaf, there are no completed components yet, so .
Now process children one by one. Suppose we are combining vertex with a child . Take an existing state for the processed part of , and a state from the child subtree.
There are exactly two choices for the edge .
First, keep the edge. Then the child's open component merges into 's open component. No new completed component is created, so the completed-component parity mask becomes
So we add
to the new state .
Second, cut the edge. Then the child's open component becomes a completed component, so it must be valid. Its XOR is
If this equals some , then cutting is legal, and we must also toggle bit because we just added one completed component of type . The new mask is
If is not in , that cut choice is illegal.
After all children are merged, is complete. At the root, the open component also has to become a real component, so for each mask we check whether
belongs to . If yes, add to the answer.
This counts every edge set exactly once: each edge is processed at its parent, and the transition chooses either cut or keep. The validity condition is checked exactly when a component becomes closed: either when a child edge is cut, or at the root for the final open component. No hidden double-counting, no magic, no nonsense.
A small implementation detail: keep masks by indices of , not by compressed XOR values. This stays clean even when is in or different masks have the same XOR value.
There are at most masks. Each edge combines two mask arrays, so the complexity is
which is at most about . Memory is .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
const ll MOD = 1000000007LL;
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<vector<int>> g(n);
for (int i = 0; i < n - 1; i++) {
int v, u;
cin >> v >> u;
--v;
--u;
g[v].push_back(u);
g[u].push_back(v);
}
vector<int> a(n), b(k);
for (int &x : a) cin >> x;
for (int &x : b) cin >> x;
int states = 1 << k;
vector<int> val(states, 0);
for (int mask = 1; mask < states; mask++) {
int bit = __builtin_ctz(mask);
val[mask] = val[mask ^ (1 << bit)] ^ b[bit];
}
vector<int> parent(n, -1), order;
order.reserve(n);
parent[0] = -2;
vector<int> st = {0};
while (!st.empty()) {
int v = st.back();
st.pop_back();
order.push_back(v);
for (int to : g[v]) {
if (parent[to] == -1) {
parent[to] = v;
st.push_back(to);
}
}
}
vector<int> sub(n, 0);
vector<array<ll, 16>> dp(n);
for (int it = n - 1; it >= 0; it--) {
int v = order[it];
sub[v] = a[v];
dp[v].fill(0);
dp[v][0] = 1;
for (int u : g[v]) {
if (parent[u] != v) continue;
sub[v] ^= sub[u];
int cut_bit[16];
for (int mask = 0; mask < states; mask++) {
cut_bit[mask] = -1;
int open = sub[u] ^ val[mask];
for (int j = 0; j < k; j++) {
if (open == b[j]) {
cut_bit[mask] = 1 << j;
break;
}
}
}
array<ll, 16> ndp{};
ndp.fill(0);
for (int p = 0; p < states; p++) {
if (dp[v][p] == 0) continue;
for (int q = 0; q < states; q++) {
if (dp[u][q] == 0) continue;
ll ways = dp[v][p] * dp[u][q] % MOD;
int base = p ^ q;
ndp[base] += ways;
if (ndp[base] >= MOD) ndp[base] -= MOD;
if (cut_bit[q] != -1) {
int nm = base ^ cut_bit[q];
ndp[nm] += ways;
if (ndp[nm] >= MOD) ndp[nm] -= MOD;
}
}
}
dp[v] = ndp;
}
}
ll ans = 0;
for (int mask = 0; mask < states; mask++) {
int open = sub[0] ^ val[mask];
bool ok = false;
for (int x : b) ok = ok || (open == x);
if (ok) {
ans += dp[0][mask];
if (ans >= MOD) ans -= MOD;
}
}
cout << ans << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
const ll MOD = 1000000007LL;
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<vector<int>> g(n);
for (int i = 0; i < n - 1; i++) {
int v, u;
cin >> v >> u;
--v;
--u;
g[v].push_back(u);
g[u].push_back(v);
}
vector<int> a(n), b(k);
for (int &x : a) cin >> x;
for (int &x : b) cin >> x;
int states = 1 << k;
vector<int> val(states, 0);
for (int mask = 1; mask < states; mask++) {
int bit = __builtin_ctz(mask);
val[mask] = val[mask ^ (1 << bit)] ^ b[bit];
}
vector<int> parent(n, -1), order;
order.reserve(n);
parent[0] = -2;
vector<int> st = {0};
while (!st.empty()) {
int v = st.back();
st.pop_back();
order.push_back(v);
for (int to : g[v]) {
if (parent[to] == -1) {
parent[to] = v;
st.push_back(to);
}
}
}
vector<int> sub(n, 0);
vector<array<ll, 16>> dp(n);
for (int it = n - 1; it >= 0; it--) {
int v = order[it];
sub[v] = a[v];
dp[v].fill(0);
dp[v][0] = 1;
for (int u : g[v]) {
if (parent[u] != v) continue;
sub[v] ^= sub[u];
int cut_bit[16];
for (int mask = 0; mask < states; mask++) {
cut_bit[mask] = -1;
int open = sub[u] ^ val[mask];
for (int j = 0; j < k; j++) {
if (open == b[j]) {
cut_bit[mask] = 1 << j;
break;
}
}
}
array<ll, 16> ndp{};
ndp.fill(0);
for (int p = 0; p < states; p++) {
if (dp[v][p] == 0) continue;
for (int q = 0; q < states; q++) {
if (dp[u][q] == 0) continue;
ll ways = dp[v][p] * dp[u][q] % MOD;
int base = p ^ q;
ndp[base] += ways;
if (ndp[base] >= MOD) ndp[base] -= MOD;
if (cut_bit[q] != -1) {
int nm = base ^ cut_bit[q];
ndp[nm] += ways;
if (ndp[nm] >= MOD) ndp[nm] -= MOD;
}
}
}
dp[v] = ndp;
}
}
ll ans = 0;
for (int mask = 0; mask < states; mask++) {
int open = sub[0] ^ val[mask];
bool ok = false;
for (int x : b) ok = ok || (open == x);
if (ok) {
ans += dp[0][mask];
if (ans >= MOD) ans -= MOD;
}
}
cout << ans << '\n';
}
}