目录
一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
二、解题报告
1、思路分析
2、复杂度
3、代码详解
一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
码题集OJ-异或 (matiji.net)
二、解题报告
1、思路分析
考虑每个结点u,其子树内距离其在k内结点对其贡献
由于是异或,所以想到按位拆分考虑贡献
我们考虑dfs,由于dfs每次沿着一条路径来走,我们可以用栈保存当前正在走的路径
维护一个差分数组diff[u][i][0 / 1] 代表 结点 u 所在子树内有效结点 第i位为1的结点数目
遍历到每个结点对<stk[top - k - 2], u> 这个区间进行差分修改即可
然后再做一次dfs 计算差分数组的前缀和
2、复杂度
时间复杂度: O(NlogU)空间复杂度:O(NlogU)
3、代码详解
#include <bits/stdc++.h>
// #include <ranges>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
constexpr int P = 1'000'000'007;
void solve() {
int n, q, k;
std::cin >> n >> q >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) std::cin >> a[i];
std::vector<std::vector<int>> adj(n);
for (int i = 0, u, v; i + 1 < n; ++ i) {
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v), adj[v].push_back(u);
}
std::vector<int> ans(n), st;
std::vector<std::vector<std::array<int, 2>>> diff(n, std::vector<std::array<int, 2>>(30));
auto dfs = [&](auto &&self, int u, int p) -> void {
st.push_back(u);
for (int i = 0; i < 30; ++ i)
++ diff[u][i][a[u] >> i & 1];
if (st.size() > k + 1)
for (int i = 0; i < 30; ++ i)
-- diff[st[st.size() - k - 2]][i][a[u] >> i & 1];
for (int v : adj[u]) {
if (v == p) continue;
self(self, v, u);
}
st.pop_back();
};
dfs(dfs, 0, -1);
auto dfs1 = [&](auto &&self, int u, int p) -> void {
for (int v : adj[u]) {
if (v == p) continue;
self(self, v, u);
for (int i = 0; i < 30; ++ i) {
diff[u][i][0] += diff[v][i][0];
diff[u][i][1] += diff[v][i][1];
}
}
};
dfs1(dfs1, 0, -1);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < 30; ++ j) {
ans[i] = (ans[i] + 1LL * diff[i][j][0] * diff[i][j][1] % P * (1LL << j) % P) % P;
}
}
for (int i = 0, x; i < q; ++ i) {
std::cin >> x;
std::cout << ans[x - 1] << '\n';
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) {
solve();
}
return 0;
}