题目链接: P3401 洛谷树
大致题意
给定一棵有 n n n个节点的树, 每条边有边权 w i w_i wi. 有 m m m次如下操作:
1 a b
询问
a
,
b
a, b
a,b路径上, 所有子路径边权异或和的和.
2 a b c
把
(
a
,
b
)
(a, b)
(a,b)这条边的权值修改为
c
c
c.
特别的, 每一时刻树中边权均 ∈ [ 0 , 1023 ] \in[0, 1023] ∈[0,1023]
解题思路
思维
首先对于树上问题, 我们考虑化为序列问题求解.
我们发现并不好在10进制下处理出所有区间异或和. 因此考虑拆位.
到此, 我们把问题变为:
给定一个长度为 n n n的01序列, 有 m m m次如下操作:
1 l r
询问 [ l , r ] [l, r] [l,r]区间的所有连续子数组的异或和的和.
2 l r c
把 [ l , r ] [l, r] [l,r]区间的权值修改为 c c c.
我们发现, 难点就是在于询问操作. 朴素的暴力方式会到达 O ( n 2 ) O(n^2) O(n2)的复杂度.
考虑优化: 我们发现其实这是一个求区间异或值的问题. 一般对于这种情况, 我们不妨处理出前缀和, 对其前缀和形式进行讨论.
对于前缀和数组而言, 一段区间的异或和相当于两点的异或和.
由此我们进行转化: 区间的所有连续子数组异或和 -> 前缀和数组中任意两点的异或和.
我们不难发现, 对于前缀和数组而言, 只有当 0 x o r 1 0 \ xor \ 1 0 xor 1时会产生贡献, 因此产生的总贡献为 c o u 0 × c o u 1 cou0 \times cou1 cou0×cou1. (0的数目 乘 1的数目)
再加上区间修改操作, 我们不难想到可以通过线段树来维护信息.
再考虑到扩展版本, 如果不是 01 01 01序列, 我们可以拆位来用多棵线段树维护.
树链剖分
对于本题的序列版本我们已经在上文中解决了, 因此我们不难想到用树链剖分把树上问题转化为序列问题求解.
但是本题还是有很多细节在里面:
- 首先所有的权值在边上, 而不是在点上, 因此我们需要把树中边权 -> 点权. 然后处理出树中前缀异或和.
- 对于修改操作, 我们发现在树中影响的节点会是一棵子树, 若修改前节点的值为
x
x
x, 修改后为
y
y
y, 则我们需要对
x
x
x和
y
y
y中二进制位不相同的位进行修改.
对其子树的影响: 假设为第 k k k位, 那么当前节点及其子树 前缀和第 k k k位必然会翻转. (因此树中维护一个区间翻转懒标记即可) - 对于查询操作, 我们虽然把树中的边权化为了点权, 但是由于我们统计的是其前缀和数组中的 0 0 0和 1 1 1的个数, 因此 L C A LCA LCA位置的信息仍然需要统计.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 3E4 + 10, B = 9;
int w[N], val[N];
struct EDGE { int to, c; };
vector<EDGE> edge[N]; //树上各个点之间的边
int p[N], dep[N], sz[N], son[N];
// 父节点 深度 节点大小 重儿子
void dfs1(int x = 1, int fa = 0) { // x = 树根节点
p[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1;
for (auto& [to, c] : edge[x]) {
if (to == fa) continue;
val[to] = c; w[to] = w[x] ^ c;
dfs1(to, x);
sz[x] += sz[to]; // 特别的, 如果边权->点权, 应记录w[to] = 边权.
if (sz[to] > sz[son[x]]) son[x] = to; //更新重儿子
}
}
int id[N], nw[N], top[N], ind;
// 新编号 新值 重链顶 当前用到的编号
void dfs2(int x = 1, int tp = 1) { // x = 树根节点, tp = 树根节点
id[x] = ++ind, nw[ind] = w[x], top[x] = tp;
if (!son[x]) return; //叶子结点
dfs2(son[x], tp); //先遍历重儿子
for (auto& [to, c] : edge[x]) {
if (to == p[x] or to == son[x]) continue;
dfs2(to, to);
}
}
struct node {
int l, r;
int cou[B + 1];
int flag;
}t[N << 2];
void pushdown(node& op, int b) {
op.cou[b] = op.r - op.l + 1 - op.cou[b];
op.flag ^= 1 << b;
}
void pushdown(int x) {
if (!t[x].flag) return;
for (int i = 0; i <= B; ++i) {
if (t[x].flag >> i & 1) pushdown(t[x << 1], i), pushdown(t[x << 1 | 1], i);
}
t[x].flag = 0;
}
void pushup(node& p, node& l, node& r) { for (int i = 0; i <= B; ++i) p.cou[i] = l.cou[i] + r.cou[i]; }
void pushup(int x) { pushup(t[x], t[x << 1], t[x << 1 | 1]); }
void build(int l, int r, int x = 1) {
t[x] = { l, r }; t[x].flag = 0;
if (l == r) {
for (int i = 0; i <= B; ++i) t[x].cou[i] = nw[l] >> i & 1;
return;
}
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int l, int r, int c, int x = 1) {
if (l <= t[x].l and r >= t[x].r) {
pushdown(t[x], c);
return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
pushup(x);
}
auto ask(int l, int r, int x = 1) {
if (l <= t[x].l and r >= t[x].r) return t[x];
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (r <= mid) return ask(l, r, x << 1);
if (l > mid) return ask(l, r, x << 1 | 1);
node left = ask(l, r, x << 1), right = ask(l, r, x << 1 | 1);
node res = { left.l, right.r };
pushup(res, left, right);
return res;
}
void modify_route(int a, int b, int c) {
if (dep[a] < dep[b]) swap(a, b);
for (int i = 0; i <= B; ++i) {
if ((val[a] ^ c) >> i & 1) {
modify(id[a], id[a] + sz[a] - 1, i);
}
}
val[a] = c;
}
int cou1[B + 1], cou0[B + 1];
ll ask_route(int a, int b) {
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
auto qaq = ask(id[top[a]], id[a]);
for (int i = 0; i <= B; ++i) {
cou1[i] += qaq.cou[i];
cou0[i] += (qaq.r - qaq.l + 1) - qaq.cou[i];
}
a = p[top[a]];
}
if (id[a] > id[b]) swap(a, b);
auto qaq = ask(id[a], id[b]);
ll res = 0;
for (int i = 0; i <= B; ++i) {
cou1[i] += qaq.cou[i];
cou0[i] += (qaq.r - qaq.l + 1) - qaq.cou[i];
res += 1ll * cou1[i] * cou0[i] * (1 << i);
cou1[i] = cou0[i] = 0;
}
return res;
}
int main()
{
int n, m; cin >> n >> m;
rep(i, n - 1) {
int a, b, c; scanf("%d %d %d", &a, &b, &c);
edge[a].push_back({ b, c }), edge[b].push_back({ a, c });
}
dfs1();
dfs2();
build(1, n);
rep(i, m) {
int tp; scanf("%d", &tp);
if (tp == 1) {
int a, b; scanf("%d %d", &a, &b);
printf("%lld\n", ask_route(a, b));
}
else {
int a, b, c; scanf("%d %d %d", &a, &b, &c);
modify_route(a, b, c);
}
}
return 0;
}