【笔记】树链剖分
轻重链剖分
将树剖分成若干个链的集合
我们定义,对于节点 u ,其所有子节点中子树最大的节点为其“重子节点”,其余子节点称为“轻子节点”;定义节点到其重子节点的边为“重边”,到其轻子节点的边为“轻边”;定义由重边相连的最大节点集合为“重链”,特别地,单个节点也算重链。
于是我们的树就被剖分成 logN 条重链和 logN 条轻边;对于每条重链,其顶端连着一条轻边;对于每个节点,其必在一条重链上
考虑如何组织这坨面条。显然我们只需要按重链优先的原则做 dfs 就好了
另外考虑这个 dfs 序,显然每个节点后面跟着的一串都是其子树节点,于是我们就能维护子树的信息了(dfs 序本来就能做这个...)
对于维护树上路径,每次更新掉一条重链,且保证重链顶不会高于两者的 lca ,最后两者跳到同一条重链上,最后更新一次就好了。
son[] 为重子节点,topf[] 为节点所在重链的链顶,id[] 为节点的 dfs 序 (多组数据的话 tot 别忘了清零!!!)
dfs1() 求 son[] ,dfs2() 求 id[] 以及向线段树初始化节点信息
#include <cstdio>
using namespace std;
const int MAXN = 100005;
int N, M, rt, mod, fst[MAXN], A[MAXN];
int dep[MAXN], fa[MAXN], sz[MAXN], son[MAXN];
int id[MAXN], B[MAXN], tot, topf[MAXN];
struct segmentTree {
#define lson (x<<1)
#define rson (x<<1|1)
int a[MAXN], sum[MAXN<<2], tag[MAXN<<2];
void build(int x, int l, int r) {
if (l==r) sum[x] = a[l];
else {
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid+1, r);
pushup(x);
}
}
// ...
} ST;
// edge, adde...
void dfs1(int x, int f, int d)
{
fa[x] = f, dep[x] = d, sz[x] = 1, son[x] = 0;
for (int o=fst[x]; o; o=e[o].pre) if (e[o].v!=f) {
dfs1(e[o].v, x, d+1);
sz[x] += sz[e[o].v];
if (sz[e[o].v]> sz[son[x]]) son[x] = e[o].v;
}
}
void dfs2(int x, int tf) {
id[x] = ++tot;
ST.a[tot] = A[x];
topf[x] = tf;
if (!son[x]) return;
dfs2(son[x], tf);
for (int o=fst[x]; o; o=e[o].pre) {
if (e[o].v!=fa[x] && e[o].v!=son[x]) dfs2(e[o].v, e[o].v);
}
}
void update1(int a, int b, int c)
{
while (topf[a]!=topf[b]) {
if (dep[topf[a]]< dep[topf[b]]) a ^= b ^= a ^= b;
ST.update(1, 1, N, id[topf[a]], id[a], c);
a = fa[topf[a]];
}
if (dep[a]< dep[b]) a ^= b ^= a ^= b;
ST.update(1, 1, N, id[b], id[a], c);
}
int query1(int a, int b)
{
int ans = 0;
while (topf[a]!=topf[b]) {
if (dep[topf[a]]< dep[topf[b]]) a ^= b ^= a ^= b;
ans = (ans + ST.query(1, 1, N, id[topf[a]], id[a])) % mod;
a = fa[topf[a]];
}
if (dep[a]< dep[b]) a ^= b ^= a ^= b;
ans = (ans + ST.query(1, 1, N, id[b], id[a])) % mod;
return (ans+mod)%mod;
}
void update2(int a, int c)
{
ST.update(1, 1, N, id[a], id[a]+sz[a]-1, c);
}
int query2(int a)
{
return (ST.query(1, 1, N, id[a], id[a]+sz[a]-1) + mod) % mod;
}
int main()
{
scanf("%d%d%d%d", &N, &M, &rt, &mod);
for (int i=1; i<=N; i++) scanf("%d", &A[i]);
for (int i=1; i< N; i++) {
int a, b; scanf("%d%d", &a, &b);
adde(a, b, i), adde(b, a, i+N);
}
// 一般 rt 为 1 就行,这题有要求罢了
dfs1(rt, 0, 0), dfs2(rt, rt);
ST.build(1, 1, N);
for (; M; M--) {
int opt, x, y, z; scanf("%d", &opt);
if (opt==1) { // 路径加
scanf("%d%d%d", &x, &y, &z);
update1(x, y, z%mod);
}
if (opt==2) { // 路径查询
scanf("%d%d", &x, &y);
printf("%d\n", query1(x, y));
}
if (opt==3) { // 子树加
scanf("%d%d", &x, &z);
update2(x, z%mod);
}
if (opt==4) { // 子树查询
scanf("%d", &x);
printf("%d\n", query2(x));
}
}
}

浙公网安备 33010602011771号