树链剖分
\(\text{luogu-3384}\)
如题,已知一棵包含 \(N\) 个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
-
1 x y z,表示将树从 \(x\) 到 \(y\) 结点最短路径上所有节点的值都加上 \(z\)。 -
2 x y,表示求树从 \(x\) 到 \(y\) 结点最短路径上所有节点的值之和。 -
3 x z,表示将以 \(x\) 为根节点的子树内所有节点值都加上 \(z\)。 -
4 x,表示求以 \(x\) 为根节点的子树内所有节点值之和。
\(1\le N \leq {10}^5\),\(1\le M \leq {10}^5\),\(1\le R\le N\),\(1\le P \le 2^{30}\)。
重链剖分模板题。
以下部分题解来自于 题解 P3384 - 洛谷专栏,经过了加工完善。
前置知识:lca、树形 dp、dfs 序、链式前向星、线段树。
概况
树链剖分就是对一棵树分成几条链,把树形变为线性,减少处理难度。
需要处理的问题:
- 将 \(x \to y\) 上所有节点的值都加上 \(z\)。
- 求 \(x \to y\) 上所有节点的值之和。
- 将以 \(x\) 为根节点的子树内所有节点值都加上 \(z\)。
- 求以 \(x\) 为根节点的子树内所有节点值之和。
概念
- 重儿子:对于每一个非叶子节点,它的儿子中子树节点个数最多的儿子为该节点的重儿子。
- 轻儿子:对于每一个非叶子节点,它的儿子中的非重儿子即为轻儿子。
- 叶子节点没有重儿子也没有轻儿子(因为它没有儿子)。
- 重边:连接任意两个重儿子的边叫做重边。
- 轻边:剩下的即为轻边。
- 重链:相邻重边连起来的 连接一条重儿子 的链叫重链。
- 对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为 \(1\) 的链。
- 每一条重链以轻儿子为起点。

第一遍 \(\text{dfs}\)
要处理的几件事情:
- 记录每个点的深度 \(dep_x\)。
- 记录每个点的父亲 \(fa_x\)。
- 记录每个非叶子节点的子树大小(含它自己)。
- 记录每个非叶子节点的重儿子编号 \(son_x\)。
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
第二遍 \(\text{dfs}\)
要处理的几件事情
- 标记每个点的新编号 \(dfn_x\)。
- 赋值每个点的初始值到新编号上 \(w_x\)。
- 处理每个点所在链的顶端 \(top_x\)。
- 处理每条链。
先处理重儿子再处理轻儿子。
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
处理问题
前面说到第二遍 \(\text{dfs}\) 的顺序是先处理重儿子再处理轻儿子,我们来模拟一下:

- 因为顺序是先重再轻,所以每一条重链的新编号是连续的。
- 因为是 \(\text{dfs}\),所以每一个子树的新编号也是连续的。
现在回顾一下我们要处理的问题。
- 处理任意两点间路径上的点权和。
- 处理一点及其子树的点权和。
- 修改任意两点间路径上的点权。
- 修改一点及其子树的点权。
当我们要处理任意两点间路径时:设所在链顶端的深度更深的那个点为 \(x\) 点。
- \(res\) 加上 \(x\) 点到 \(top_x\) 这一段区间的点权和。
- 把 \(x\) 跳到 \(fa_{top_x}\)。
不停执行这两个步骤,直到两个点处于一条链上,这时再加上此时两个点的区间和即可。

用线段树处理这部分的操作。每次查询时间复杂度为 \(O(\log^2 n)\)。
ll qryrg(ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = (res + qry(1, dfn[top[x]], dfn[x])) % MOD;
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = (res + qry(1, dfn[x], dfn[y])) % MOD;
return res;
}
处理一点及其子树的点权和:实际上 \(x\) 的子树的编号范围就是 \(dfn_x \sim dfn_x + sz_x - 1\)。
于是直接线段树区间查询即可。时间复杂度为 \(O(\log n)\)。
ll qryson(ll x) { return qry(1, dfn[x], dfn[x] + sz[x] - 1); }
当然,区间修改就和区间查询一样的。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAXN 100005
#define ll long long
long long read() {
long long x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll n, m, rt, MOD, a[MAXN], son[MAXN], f[MAXN], sz[MAXN], dep[MAXN];
struct node { long long l, r, w, lz; } t[MAXN << 2];
ll cnt, dfn[MAXN], w[MAXN], top[MAXN];
vector<ll> v[MAXN];
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
void build(ll p, ll l, ll r) {
t[p].l = l, t[p].r = r, t[p].lz = 0;
if(l == r) { t[p].w = w[l]; return; }
ll mid = (l + r) >> 1;
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
t[p].w = (t[p << 1].w + t[p << 1 | 1].w) % MOD;
return;
}
void pushdown(ll p) {
ll lz = t[p].lz;
(t[p << 1].lz += lz) %= MOD, (t[p << 1 | 1].lz += lz) %= MOD;
(t[p << 1].w += lz * (t[p << 1].r - t[p << 1].l + 1) % MOD) %= MOD;
(t[p << 1 | 1].w += lz * (t[p << 1 | 1].r - t[p << 1 | 1].l + 1) % MOD) %= MOD;
t[p].lz = 0; return;
}
void upd(ll p, ll l, ll r, ll x) {
if(l <= t[p].l && t[p].r <= r) {
t[p].w = (t[p].w + x * (t[p].r - t[p].l + 1)) % MOD;
t[p].lz = (t[p].lz + x) % MOD; return;
}
ll mid = (t[p].l + t[p].r) >> 1;
if(t[p].lz) pushdown(p);
if(l <= mid) upd(p << 1, l, r, x);
if(r > mid) upd(p << 1 | 1, l, r, x);
t[p].w = (t[p << 1].w + t[p << 1 | 1].w) % MOD;
return;
}
ll qry(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].w % MOD;
ll mid = (t[p].l + t[p].r) >> 1, res = 0;
if(t[p].lz) pushdown(p);
if(l <= mid) res = (res + qry(p << 1, l, r)) % MOD;
if(r > mid) res = (res + qry(p << 1 | 1, l, r)) % MOD;
return res;
}
void updrg(ll x, ll y, ll z) {
z %= MOD;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
upd(1, dfn[top[x]], dfn[x], z);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
upd(1, dfn[x], dfn[y], z);
return;
}
ll qryrg(ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = (res + qry(1, dfn[top[x]], dfn[x])) % MOD;
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = (res + qry(1, dfn[x], dfn[y])) % MOD;
return res;
}
void updson(ll x, ll z) {
upd(1, dfn[x], dfn[x] + sz[x] - 1, z);
return;
}
ll qryson(ll x) { return qry(1, dfn[x], dfn[x] + sz[x] - 1); }
int main() {
n = read(), m = read(), rt = read(), MOD = read();
for(int i = 1; i <= n; i ++) a[i] = read() % MOD;
for(int i = 1; i < n; i ++) {
ll x = read(), y = read();
v[x].push_back(y), v[y].push_back(x);
}
dfs1(rt, 0), dfs2(rt, rt), build(1, 1, n);
while(m --) {
ll op = read(), x, y, z;
if(op == 1) x = read(), y = read(), z = read(), updrg(x, y, z);
else if(op == 2) x = read(), y = read(), cout << qryrg(x, y) << "\n";
else if(op == 3) x = read(), y = read(), updson(x, y);
else x = read(), cout << qryson(x) << "\n";
}
return 0;
}
\(\text{luogu-2590}\)
一棵树上有 \(n\) 个节点,编号分别为 \(1\) 到 \(n\),每个节点都有一个权值 \(w\)。
我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点 \(u\) 的权值改为 \(t\)。
II. QMAX u v: 询问从点 \(u\) 到点 \(v\) 的路径上的节点的最大权值。
III. QSUM u v: 询问从点 \(u\) 到点 \(v\) 的路径上的节点的权值和。
注意:从点 \(u\) 到点 \(v\) 的路径上的节点包括 \(u\) 和 \(v\) 本身。
\(1\le n \le 3\times 10^4\),\(0\le q\le 2\times 10^5\),\(-3 \times 10^4 \le w_i \le 3 \times 10^4\)。
树剖+线段树。
直接树剖转化为区间上的问题,然后用线段树维护区间信息即可。
单点修改甚至不需要懒标记。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
#define MAXN 30005
#define INF 0x3f3f3f3f
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll n, q, a[MAXN], f[MAXN], dep[MAXN], sz[MAXN], son[MAXN];
struct node { ll l, r, x, mx; } t[MAXN << 2];
ll dfn[MAXN], w[MAXN], top[MAXN], cnt;
vector<ll> v[MAXN];
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] >= sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
void build(ll p, ll l, ll r) {
t[p] = {l, r, 0, -INF};
if(l == r) { t[p].x = t[p].mx = w[l]; return; }
ll mid = (l + r) >> 1;
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
t[p].mx = max(t[p << 1].mx, t[p << 1 | 1].mx);
return;
}
void upd(ll p, ll x, ll k) {
if(t[p].l == t[p].r) { t[p].x = k, t[p].mx = k; return; }
ll mid = (t[p].l + t[p].r) >> 1;
if(x <= mid) upd(p << 1, x, k);
else upd(p << 1 | 1, x, k);
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
t[p].mx = max(t[p << 1].mx, t[p << 1 | 1].mx);
return;
}
ll qmx(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].mx;
ll mid = (t[p].l + t[p].r) >> 1, res = -INF;
if(l <= mid) res = max(res, qmx(p << 1, l, r));
if(r > mid) res = max(res, qmx(p << 1 | 1, l, r));
return res;
}
ll qsum(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].x;
ll mid = (t[p].l + t[p].r) >> 1, res = 0;
if(l <= mid) res += qsum(p << 1, l, r);
if(r > mid) res += qsum(p << 1 | 1, l, r);
return res;
}
ll qrymx(ll x, ll y) {
ll res = -INF;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = max(res, qmx(1, dfn[top[x]], dfn[x]));
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = max(res, qmx(1, dfn[x], dfn[y]));
return res;
}
ll qrysum(ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res += qsum(1, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res += qsum(1, dfn[x], dfn[y]);
return res;
}
int main() {
n = read();
for(int i = 1; i < n; i ++) {
ll x = read(), y = read();
v[x].push_back(y), v[y].push_back(x);
}
for(int i = 1; i <= n; i ++) a[i] = read();
q = read(); dfs1(1, 0), dfs2(1, 1), build(1, 1, n);
while(q --) {
string s; cin >> s; ll x, y;
if(s == "CHANGE") x = read(), y = read(), upd(1, dfn[x], y);
else if(s == "QMAX") x = read(), y = read(), cout << qrymx(x, y) << "\n";
else x = read(), y = read(), cout << qrysum(x, y) << "\n";
}
return 0;
}
\(\text{luogu-3313}\)
S 国有 \(n\) 个城市,编号从 \(1\) 到 \(n\)。城市间用 \(n-1\) 条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。
为了方便,我们用不同的正整数代表各种宗教,S 国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S 国为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
在 S 国的历史上常会发生以下几种事件:
CC x c:城市 \(x\) 的居民全体改信了 \(c\) 教;CW x w:城市 \(x\) 的评级调整为 \(w\);QS x y:一位旅行者从城市 \(x\) 出发,到城市 \(y\),并记下了途中留宿过的城市的评级总和;QM x y:一位旅行者从城市 \(x\) 出发,到城市 \(y\),并记下了途中留宿过的城市的评级最大值。
由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。
\(1 \le n,q \le 10^5\),\(0 \le c_i \le 10^5\),\(0 \le w_i \le \min(10^4, c)\)。
可以树剖完用 \(\max c_i\) 棵动态开点线段树维护。
但是显然用分块写更容易一点,时间复杂度 \(O(n \sqrt n \log n)\),常数小。
不考虑树剖常数的话,可以证明最优的块长是 \(\sqrt \frac{n}{\log n}\)。
注意:块的个数大约是 \(1300\) 左右,建议开 \(1500\)。
#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
#define ll int
#define MAXN 100005
#define INF 0x3f3f3f3f
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll n, q, B, a[MAXN], b[MAXN], f[MAXN], dep[MAXN], sz[MAXN], son[MAXN];
ll dfn[MAXN], w[MAXN], top[MAXN], cnt, l[MAXN], r[MAXN];
ll bl[MAXN], s[MAXN][1505], mx[MAXN][1505], c[MAXN];
vector<ll> v[MAXN];
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], c[cnt] = b[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
void updb(ll x, ll y) {
ll k = bl[x], lst = c[x]; c[x] = y;
s[lst][k] -= w[x], s[y][k] += w[x];
mx[lst][k] = mx[y][k] = -INF;
for(int i = l[k]; i <= r[k]; i ++)
if(c[i] == lst) mx[lst][k] = max(mx[lst][k], w[i]);
else if(c[i] == y) mx[y][k] = max(mx[y][k], w[i]);
return;
}
void upda(ll x, ll y) {
ll k = bl[x]; mx[c[x]][k] = -INF;
s[c[x]][k] += y - w[x], w[x] = y;
for(int i = l[k]; i <= r[k]; i ++)
if(c[i] == c[x]) mx[c[x]][k] = max(mx[c[x]][k], w[i]);
return;
}
ll qsum(ll p, ll x, ll y) {
ll res = 0, pl = bl[x], pr = bl[y];
if(pl == pr) {
for(int i = x; i <= y; i ++) if(c[i] == p) res += w[i];
return res;
}
for(int i = x; i <= r[pl]; i ++) if(c[i] == p) res += w[i];
for(int i = l[pr]; i <= y; i ++) if(c[i] == p) res += w[i];
for(int i = pl + 1; i < pr; i ++) res += s[p][i];
return res;
}
ll qmx(ll p, ll x, ll y) {
ll res = -INF, pl = bl[x], pr = bl[y];
if(pl == pr) {
for(int i = x; i <= y; i ++) if(c[i] == p) res = max(res, w[i]);
return res;
}
for(int i = x; i <= r[pl]; i ++) if(c[i] == p) res = max(res, w[i]);
for(int i = l[pr]; i <= y; i ++) if(c[i] == p) res = max(res, w[i]);
for(int i = pl + 1; i < pr; i ++) res = max(res, mx[p][i]);
return res;
}
ll qrysum(ll p, ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res += qsum(p, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res += qsum(p, dfn[x], dfn[y]);
return res;
}
ll qrymx(ll p, ll x, ll y) {
ll res = -INF;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = max(res, qmx(p, dfn[top[x]], dfn[x]));
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = max(res, qmx(p, dfn[x], dfn[y]));
return res;
}
int main() {
n = read(), q = read(), B = sqrt(1.0 * n / log2(n));
for(int i = 1; i <= n; i ++) a[i] = read(), b[i] = read();
for(int i = 1; i < n; i ++) {
ll x = read(), y = read();
v[x].push_back(y), v[y].push_back(x);
}
dfs1(1, 0), dfs2(1, 1);
for(int i = 0; i <= n; i ++) l[i] = INF, r[i] = -INF;
for(int i = 1; i <= n; i ++) {
ll k = i / B; bl[i] = k;
l[k] = min(l[k], i), r[k] = max(r[k], i);
s[c[i]][k] += w[i], mx[c[i]][k] = max(mx[c[i]][k], w[i]);
}
while(q --) {
string s; cin >> s; ll x, y;
if(s == "CC") x = read(), y = read(), updb(dfn[x], y);
else if(s == "CW") x = read(), y = read(), upda(dfn[x], y);
else if(s == "QS") x = read(), y = read(), cout << qrysum(c[dfn[x]], x, y) << "\n";
else x = read(), y = read(), cout << qrymx(c[dfn[x]], x, y) << "\n";
}
return 0;
}
\(\text{hdu-3966}\)
给定一棵 \(n\) 个节点的树,点有点权,有以下三种操作:
I x y k将 \(x \to y\) 路径上的所有节点的点权加 \(k\)。D x y k将 \(x \to y\) 路径上的所有节点的点权减 \(k\)。Q x查询 \(x\) 节点的点权。
\(1 \le n \le 5 \times 10^5\),\(m=n-1\),\(1 \le q \le 10^5\),\(0 \le a_i,k \le 1000\)。
直接树剖,然后用线段树维护就好了,模板题。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
#define MAXN 50005
ll n, m, q, a[MAXN], f[MAXN], dep[MAXN], sz[MAXN], son[MAXN];
struct node { ll l, r, x, lz; } t[MAXN << 2];
ll dfn[MAXN], w[MAXN], top[MAXN], cnt;
vector<ll> v[MAXN];
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
void build(ll p, ll l, ll r) {
t[p] = {l, r, 0, 0};
if(l == r) { t[p].x = w[l]; return; }
ll mid = (l + r) >> 1;
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
return;
}
void pushdown(ll p) {
ll lz = t[p].lz;
t[p << 1].x += lz * (t[p << 1].r - t[p << 1].l + 1);
t[p << 1 | 1].x += lz * (t[p << 1 | 1].r - t[p << 1 | 1].l + 1);
t[p << 1].lz += lz, t[p << 1 | 1].lz += lz, t[p].lz = 0;
return;
}
void update(ll p, ll l, ll r, ll x) {
if(l <= t[p].l && t[p].r <= r) {
t[p].x += x * (t[p].r - t[p].l + 1);
t[p].lz += x; return;
}
ll mid = (t[p].l + t[p].r) >> 1;
if(t[p].lz) pushdown(p);
if(l <= mid) update(p << 1, l, r, x);
if(r > mid) update(p << 1 | 1, l, r, x);
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
return;
}
void upd(ll x, ll y, ll k) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, dfn[top[x]], dfn[x], k);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
update(1, dfn[x], dfn[y], k);
return;
}
ll qry(ll p, ll x) {
if(t[p].l == t[p].r) return t[p].x;
ll mid = (t[p].l + t[p].r) >> 1;
if(t[p].lz) pushdown(p);
if(x <= mid) return qry(p << 1, x);
return qry(p << 1 | 1, x);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
while(cin >> n >> m >> q) {
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) v[i].clear();
cnt = 0;
for(int i = 1; i <= m; i ++) {
ll x, y; cin >> x >> y;
v[x].push_back(y), v[y].push_back(x);
}
dfs1(1, 0), dfs2(1, 1), build(1, 1, n);
while(q --) {
char c; cin >> c; ll x, y, k;
if(c == 'I') cin >> x >> y >> k, upd(x, y, k);
else if(c == 'D') cin >> x >> y >> k, upd(x, y, -k);
else cin >> x, cout << qry(1, dfn[x]) << "\n";
}
}
return 0;
}
\(\text{LightOJ-1348}\)
给定一棵 \(n\) 个节点的树,点有点权,有以下三种操作:
0 x y查询 \(x \to y\) 路径上的所有点权和。1 x y将 \(x\) 节点的点权变为 \(y\)。
\(1 \le T \le 5\),\(2 \le n \le 3 \times 10^4\),\(1 \le q \le 10^5\)。
直接树剖,然后用线段树维护就好了,模板题。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll int
#define MAXN 30005
#define INF 0x3f3f3f3f
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll T, n, q, a[MAXN], f[MAXN], dep[MAXN], sz[MAXN], son[MAXN];
ll dfn[MAXN], w[MAXN], top[MAXN], cnt, tot;
struct node { ll l, r, x; } t[MAXN << 2];
vector<ll> v[MAXN];
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] >= sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
void build(ll p, ll l, ll r) {
t[p] = {l, r, 0};
if(l == r) { t[p].x = w[l]; return; }
ll mid = (l + r) >> 1;
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
return;
}
void upd(ll p, ll x, ll k) {
if(t[p].l == t[p].r) { t[p].x = k; return; }
ll mid = (t[p].l + t[p].r) >> 1;
if(x <= mid) upd(p << 1, x, k);
else upd(p << 1 | 1, x, k);
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
return;
}
ll qsum(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].x;
ll mid = (t[p].l + t[p].r) >> 1, res = 0;
if(l <= mid) res += qsum(p << 1, l, r);
if(r > mid) res += qsum(p << 1 | 1, l, r);
return res;
}
ll qrysum(ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res += qsum(1, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res += qsum(1, dfn[x], dfn[y]);
return res;
}
int main() {
T = read();
while(T --) {
n = read(), cnt = 0;
for(int i = 1; i <= n; i ++) a[i] = read();
for(int i = 1; i <= n; i ++) v[i].clear();
for(int i = 1; i < n; i ++) {
ll x = read() + 1, y = read() + 1;
v[x].push_back(y), v[y].push_back(x);
}
q = read(); dfs1(1, 0), dfs2(1, 1), build(1, 1, n);
cout << "Case " << (++ tot) << ":\n";
while(q --) {
ll x = 1, y = 1;
if(read()) x += read(), y = read(), upd(1, dfn[x], y);
else x += read(), y += read(), cout << qrysum(x, y) << "\n";
}
}
return 0;
}
\(\text{luogu-1505}\)
给定一棵 \(n\) 个节点的树,边带权,编号 \(0 \sim n-1\),需要支持五种操作:
C i w将输入的第 \(i\) 条边权值改为 \(w\);N u v将 \(u,v\) 节点之间的边权都变为相反数;SUM u v询问 \(u,v\) 节点之间边权和;MAX u v询问 \(u,v\) 节点之间边权最大值;MIN u v询问 \(u,v\) 节点之间边权最小值。
保证任意时刻所有边的权值都在 \([-1000,1000]\) 内。
\(1\le n,m \le 2\times 10^5\)。
首先树剖,然后考虑怎么用线段树维护。
用线段树记录区间和、区间最大值、区间最小值以及区间反转的懒标记。
区间取相反数时,区间和变为相反数,最大值变为最小值的相反数,最小值变为最大值的相反数。
然后就做完了。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
#define MAXN 200005
#define pii pair<ll, ll>
#define fi first
#define se second
#define INF 0x3f3f3f3f
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll n, m, a[MAXN], b[MAXN], p[MAXN], f[MAXN], dep[MAXN], sz[MAXN];
struct node { ll l, r, x, mx, mn, f; } t[MAXN << 2];
ll son[MAXN], dfn[MAXN], cnt, w[MAXN], top[MAXN];
vector<pii > v[MAXN];
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto it : v[x]) if(it.fi != fa) {
ll y = it.fi, t = it.se;
p[t] = y, dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = b[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto it : v[x]) if(it.fi != son[x]
&& it.fi != f[x]) dfs2(it.fi, it.fi);
return;
}
void pushup(ll p) {
t[p].x = t[p << 1].x + t[p << 1 | 1].x;
t[p].mx = max(t[p << 1].mx, t[p << 1 | 1].mx);
t[p].mn = min(t[p << 1].mn, t[p << 1 | 1].mn);
return;
}
void pushdown(ll p) {
t[p << 1].x *= -1, t[p << 1 | 1].x *= -1;
ll mx1 = t[p << 1].mx, mx2 = t[p << 1 | 1].mx;
ll mn1 = t[p << 1].mn, mn2 = t[p << 1 | 1].mn;
t[p << 1].mx = -mn1, t[p << 1 | 1].mx = -mn2;
t[p << 1].mn = -mx1, t[p << 1 | 1].mn = -mx2;
t[p << 1].f ^= 1, t[p << 1 | 1].f ^= 1, t[p].f = 0;
return;
}
void build(ll p, ll l, ll r) {
t[p] = {l, r, 0, -INF, INF, 0};
if(l == r) {
t[p].x = t[p].mx = t[p].mn = w[l];
return;
}
ll mid = (l + r) >> 1;
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
pushup(p); return;
}
void upd(ll p, ll x, ll k) {
if(t[p].l == t[p].r) {
t[p].x = t[p].mx = t[p].mn = k;
return;
}
ll mid = (t[p].l + t[p].r) >> 1;
if(t[p].f) pushdown(p);
if(x <= mid) upd(p << 1, x, k);
else upd(p << 1 | 1, x, k);
pushup(p); return;
}
void upd1(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) {
t[p].x *= -1; ll mx = t[p].mx, mn = t[p].mn;
t[p].mx = -mn, t[p].mn = -mx, t[p].f ^= 1;
return;
}
ll mid = (t[p].l + t[p].r) >> 1;
if(t[p].f) pushdown(p);
if(l <= mid) upd1(p << 1, l, r);
if(r > mid) upd1(p << 1 | 1, l, r);
pushup(p); return;
}
void upd2(ll x, ll y) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
upd1(1, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
upd1(1, dfn[x] + 1, dfn[y]);
return;
}
ll qsum(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].x;
ll mid = (t[p].l + t[p].r) >> 1, res = 0;
if(t[p].f) pushdown(p);
if(l <= mid) res += qsum(p << 1, l, r);
if(r > mid) res += qsum(p << 1 | 1, l, r);
return res;
}
ll qmx(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].mx;
ll mid = (t[p].l + t[p].r) >> 1, res = -INF;
if(t[p].f) pushdown(p);
if(l <= mid) res = max(res, qmx(p << 1, l, r));
if(r > mid) res = max(res, qmx(p << 1 | 1, l, r));
return res;
}
ll qmn(ll p, ll l, ll r) {
if(l <= t[p].l && t[p].r <= r) return t[p].mn;
ll mid = (t[p].l + t[p].r) >> 1, res = INF;
if(t[p].f) pushdown(p);
if(l <= mid) res = min(res, qmn(p << 1, l, r));
if(r > mid) res = min(res, qmn(p << 1 | 1, l, r));
return res;
}
ll qrysum(ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res += qsum(1, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res += qsum(1, dfn[x] + 1, dfn[y]);
return res;
}
ll qrymx(ll x, ll y) {
ll res = -INF;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = max(res, qmx(1, dfn[top[x]], dfn[x]));
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = max(res, qmx(1, dfn[x] + 1, dfn[y]));
return res;
}
ll qrymn(ll x, ll y) {
ll res = INF;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res = min(res, qmn(1, dfn[top[x]], dfn[x]));
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
res = min(res, qmn(1, dfn[x] + 1, dfn[y]));
return res;
}
int main() {
n = read();
for(int i = 1; i < n; i ++) {
ll x = read() + 1, y = read() + 1, t = read(); a[i] = t;
v[x].push_back({y, i}), v[y].push_back({x, i});
}
m = read(), dfs1(1, 0);
for(int i = 1; i < n; i ++) b[p[i]] = a[i];
dfs2(1, 1), build(1, 1, n);
while(m --) {
string s; cin >> s; ll x = 1, y = 1;
if(s == "C") x = read(), y = read(), upd(1, dfn[p[x]], y);
else if(s == "N") x += read(), y += read(), upd2(x, y);
else if(s == "SUM") x += read(), y += read(), cout << qrysum(x, y) << "\n";
else if(s == "MAX") x += read(), y += read(), cout << qrymx(x, y) << "\n";
else x += read(), y += read(), cout << qrymn(x, y) << "\n";
}
return 0;
}
\(\text{luogu-2386}\)
给定一棵 \(n\) 个节点的无根树,共有 \(m\) 个操作,操作分为两种:
- 将节点 \(a\) 到节点 \(b\) 的路径上的所有点(包括 \(a\) 和 \(b\))都染成颜色 \(c\)。
- 询问节点 \(a\) 到节点 \(b\) 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:11、222、1。
\(1 \leq n, m \leq 10^5\),\(1 \leq w_i, c \leq 10^9\),\(1 \leq a, b, u, v \leq n\)。
首先树剖,然后用 ODT 维护即可。树剖分成若干段之后,注意去重。
#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
using namespace std;
#define ll long long
#define MAXN 100005
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll n, q, a[MAXN], f[MAXN], dep[MAXN], sz[MAXN], son[MAXN];
ll dfn[MAXN], cnt, top[MAXN], w[MAXN];
struct node { ll l, r, x; };
vector<ll> v[MAXN];
set<node> s;
bool operator < (const node &x, const node &y) {
return x.l < y.l;
}
void dfs1(ll x, ll fa) {
f[x] = fa, dep[x] = dep[fa] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != fa) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != f[x]) dfs2(y, y);
return;
}
auto split(ll x) {
auto p = s.lower_bound({x, 0, 0});
if(p != s.end() && p->l == x) return p;
p --; ll l = p->l, r = p->r, w = p->x;
s.erase(p), s.insert({l, x - 1, w});
return s.insert({x, r, w}).first;
}
void assign(ll l, ll r, ll x) {
auto pr = split(r + 1), pl = split(l);
s.erase(pl, pr), s.insert({l, r, x});
return;
}
ll ask(ll l, ll r) {
auto pr = split(r + 1), pl = split(l);
ll lst = -1, res = 0;
while(pl != pr) {
if(pl->x != lst) res ++, lst = pl->x;
pl ++;
}
return res;
}
ll cg(ll x) {
auto p = s.lower_bound({x, 0, 0});
if(p != s.end() && p->l == x) return p->x;
p --; return p->x;
}
void upd(ll x, ll y, ll z) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
assign(dfn[top[x]], dfn[x], z);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
assign(dfn[x], dfn[y], z);
return;
}
ll qry(ll x, ll y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
res += ask(dfn[top[x]], dfn[x]);
ll t = f[top[x]];
if(cg(dfn[top[x]]) == cg(dfn[t])) res --;
x = t;
}
if(dep[x] > dep[y]) swap(x, y);
res += ask(dfn[x], dfn[y]);
return res;
}
int main() {
n = read(), q = read();
for(int i = 1; i <= n; i ++) a[i] = read();
for(int i = 1; i < n; i ++) {
ll x = read(), y = read();
v[x].push_back(y), v[y].push_back(x);
}
dfs1(1, 0), dfs2(1, 1);
for(int i = 1; i <= n; i ++) s.insert({i, i, w[i]});
s.insert({n + 1, n + 1, -1});
while(q --) {
char c; cin >> c; ll x = read(), y = read(), z;
if(c == 'C') z = read(), upd(x, y, z);
else cout << qry(x, y) << "\n";
}
return 0;
}
\(\text{luogu-3258}\)
松鼠的新家是一棵树,前几天刚刚装修了新家,新家有 \(n\) 个房间,并且有 \(n-1\) 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。
松鼠想邀请小熊前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去 \(a_1\),再去 \(a_2\),……,最后到 \(a_n\),去参观新家。可是这样会导致重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。
维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。
因为松鼠参观指南上的最后一个房间 \(a_n\) 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。
\(2 \le n \le 3 \times 10^5\),\(1 \le a_i \le n\)。
树剖专题里怎么混了道绿题。
不过确实可以用树剖做,就是直接剖完之后用线段树维护区间加即可。
但是显然有更简单的做法,写个 lca,树上差分维护就好了,注意别算重了。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
#define MAXN 300005
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
ll n, a[MAXN], b[MAXN], dep[MAXN], f[MAXN][20], lg[MAXN];
vector<ll> v[MAXN];
void dfs(ll x, ll fa) {
f[x][0] = fa, dep[x] = dep[fa] + 1;
for(int i = 1; i <= lg[dep[x]]; i ++)
f[x][i] = f[f[x][i - 1]][i - 1];
for(auto y : v[x]) if(y != fa) dfs(y, x);
return;
}
ll lca(ll x, ll y) {
if(dep[x] < dep[y]) swap(x, y);
while(dep[x] > dep[y])
x = f[x][lg[dep[x] - dep[y]] - 1];
if(x == y) return x;
for(int i = lg[dep[x]] - 1; i >= 0; i --)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
void dfs2(ll x) {
for(auto y : v[x]) if(y != f[x][0]) dfs2(y), b[x] += b[y];
return;
}
int main() {
n = read();
for(int i = 1; i <= n; i ++)
a[i] = read(), lg[i] = lg[i >> 1] + 1;
for(int i = 1; i < n; i ++) {
ll x = read(), y = read();
v[x].push_back(y), v[y].push_back(x);
}
dfs(1, 0);
for(int i = 1; i < n; i ++) {
ll x = a[i], y = a[i + 1], z = lca(x, y);
b[x] ++, b[y] ++, b[z] --, b[f[z][0]] --;
}
dfs2(1);
for(int i = 2; i <= n; i ++) b[a[i]] --;
for(int i = 1; i <= n; i ++) cout << b[i] << "\n";
return 0;
}
\(\text{luogu-5354}\)
给你一个有 \(n\) 个点的树,每个点的包括一个位运算 \(opt\) 和一个权值 \(x\),位运算有&,|,^ 三种,分别用 \(1,2,3\) 表示。
每次询问包含三个整数 \(x,y,z\),初始选定一个数 \(v\)。然后 \(v\) 依次经过从 \(x\) 到 \(y\) 的所有节点,每经过一个点 \(i\),\(v\) 就变成 \(v\ opt_i\ x_i\),所以他想问你,最后到 \(y\) 时,希望得到的值尽可能大,求最大值。给定的初始值 \(v\) 必须是在 \([0,z]\) 之间。
每次修改包含三个整数 \(x,y,z\),意思是把 \(x\) 点的操作修改为 \(y\),数值改为 \(z\)。
\(0\leq n,m \leq 10^5\),\(0\leq k\leq 64\)。
以下部分题解来自于 题解 P3613 【睡觉困难综合征】 - 洛谷专栏。
首先发现每一位不会互相影响,可以把每一位分开考虑,然后用树链剖分或者 LCT 维护这个树。
修改直接修改,询问的时候算出来每一位填 \(01\) 经过这条链的变换之后得到的值。
考虑贪心,从高往低。
如果这一位填 \(0\) 可以得到 \(1\),那么填 \(0\) 一定是最优的。否则如果可以填 \(1\),就把这一位填为 \(1\)。
复杂度是 \(O(n k \log^2 n)\) 或者 \(O(nk \log n)\),只能通过 \(50\%\) 的数据。
发现可以并行计算这 \(k\) 位,复杂度降为 \(n \log^2 n\) 的树链剖分或者 \(n \log n\) 的 LCT。
这个题没有卡常,合并信息不是 \(O(1)\) 的算法没有通过是很正常的吧。
最优复杂度是 \(\log^2 n\),不过期望下大概是 \(\log n \log\log n\) 的感觉
这个题的最优复杂度为 \(O(n + q(\log n + k ))\),至少目前来说是这样的。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define ll long long
#define ull unsigned long long
#define MAXN 100005
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
return x * f;
}
struct node { ull l0, l1, r0, r1; } a[MAXN], w[MAXN], t[MAXN << 2];
ll n, m, k, fa[MAXN], dep[MAXN], sz[MAXN], son[MAXN];
ll dfn[MAXN], cnt, top[MAXN];
vector<ll> v[MAXN];
ull f(ull x, ull y, ull z) { return (z & x) | (y & ~x); }
node operator + (const node &x, const node &y) {
return {f(x.l0, y.l0, y.l1), f(x.l1, y.l0, y.l1),
f(y.r0, x.r0, x.r1), f(y.r1, x.r0, x.r1)};
}
node trans(ull x, ull y) {
if(x == 1) return {0, y, 0, y};
else if(x == 2) return {y, -1ull, y, -1ull};
return {y, ~y, y, ~y};
}
void dfs1(ll x, ll ff) {
fa[x] = ff, dep[x] = dep[ff] + 1;
sz[x] = 1, son[x] = 0;
for(auto y : v[x]) if(y != ff) {
dfs1(y, x), sz[x] += sz[y];
if(sz[y] > sz[son[x]]) son[x] = y;
}
return;
}
void dfs2(ll x, ll t) {
dfn[x] = (++ cnt), w[cnt] = a[x], top[x] = t;
if(!son[x]) return; dfs2(son[x], t);
for(auto y : v[x]) if(y != son[x] && y != fa[x]) dfs2(y, y);
return;
}
void build(ll p, ll l, ll r) {
if(l == r) { t[p] = w[l]; return; }
ll mid = (l + r) >> 1;
build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
t[p] = t[p << 1] + t[p << 1 | 1];
return;
}
void upd(ll p, ll x, node y, ll l, ll r) {
if(l == r) { t[p] = y; return; }
ll mid = (l + r) >> 1;
if(x <= mid) upd(p << 1, x, y, l, mid);
else upd(p << 1 | 1, x, y, mid + 1, r);
t[p] = t[p << 1] + t[p << 1 | 1];
return;
}
node qry(ll p, ll L, ll R, ll l, ll r) {
if(L <= l && r <= R) return t[p];
ll mid = (l + r) >> 1;
node res; res.l0 = res.r0 = 0, res.l1 = res.r1 = -1ull;
if(L <= mid) res = res + qry(p << 1, L, R, l, mid);
if(R > mid) res = res + qry(p << 1 | 1, L, R, mid + 1, r);
return res;
}
ull query(ull x, ull y, ull z) {
ull res = 0; node X, Y;
X.l0 = X.r0 = 0, X.l1 = X.r1 = -1ull;
Y.l0 = Y.r0 = 0, Y.l1 = Y.r1 = -1ull;
while(top[x] != top[y])
if(dfn[top[x]] > dfn[top[y]])
X = qry(1, dfn[top[x]], dfn[x], 1, n) + X, x = fa[top[x]];
else Y = qry(1, dfn[top[y]], dfn[y], 1, n) + Y, y = fa[top[y]];
if(dfn[x] > dfn[y]) X = qry(1, dfn[y], dfn[x], 1, n) + X;
else Y = qry(1, dfn[x], dfn[y], 1, n) + Y;
Y = (node){X.r0, X.r1, 0, 0} + Y;
for(int i = k - 1; i >= 0; i --) {
ull t = (1ull << i);
if(res + t <= z && (Y.l0 & t) < (Y.l1 & t)) res |= t;
}
return f(res, Y.l0, Y.l1);
}
int main() {
n = read(), m = read(), k = read();
for(int i = 1; i <= n; i ++) {
ull x = read(), y = read();
a[i] = trans(x, y);
}
for(int i = 1; i < n; i ++) {
ll x = read(), y = read();
v[x].push_back(y), v[y].push_back(x);
}
dfs1(1, 0), dfs2(1, 1), build(1, 1, n);
while(m --) {
ull op = read(), x = read(), y = read(), z = read();
if(op == 1) cout << query(x, y, z) << "\n";
else upd(1, dfn[x], trans(y, z), 1, n);
}
return 0;
}
本文来自博客园,作者:So_noSlack,转载请注明原文链接:https://www.cnblogs.com/So-noSlack/p/19585347

浙公网安备 33010602011771号