树链剖分用于解决树上操作问题,大致是 DFS序+线段树,没有看过的可以先了解一下 。
树剖把树中的儿子节点分为重儿子和轻儿子,树中链分为重链和轻链,目的是加快操作速率,降低时间复杂度。树剖的精华之处类似于LCA,跳来跳去提升效率,而且能做LCA做不到的功能,数组开了那么多还是很有用的。
几个概念:
- 重儿子:子树中节点最多的儿子节点
- 轻儿子:非重儿子
- 重链:由重儿子连成的节点
- 轻链:非重链
几个数组:
用下面的数组保存树的信息,具体的作用看了代码就明白了
- depth[] : 节点深度
- heavy_son[] : 节点的重儿子
- father[] : 节点的父亲
- size[] : 节点的子树数量
- id[] : 所有节点的dfs序
- rk[] : 节点在dfs序(id[])中的位置
- top[] : 每个节点所在链的顶端节点
需要两次dfs操作,第一次得出 每个节点的 depth, heavy_son, father, size
void dfs1(int u, int fa) ///size,heavy_son,depth,father
{
siz[u] = 1;
heavy_son[u] = 0;
for(int i=head[u]; i ; i=nxt[i]){
int v = to[i];
if(v == fa) continue;
depth[v] = depth[u] + 1;
father[v] = u;
dfs1(v, u);
siz[u] += siz[v];
if(siz[v] > siz[heavy_son[u]])
heavy_son[u] = v;
}
}
第二次得出每个节点的 id, rk, top
void dfs2(int u, int rt) ///id,rk,top
{
id[u] = ++Index;
rk[Index] = u;
top[u] = rt;
if(heavy_son[u]) dfs2(heavy_son[u], rt);
for(int i=head[u]; i; i=nxt[i]){
int v = to[i];
if(v == father[u] || v == heavy_son[u]) continue;
dfs2(v, v);
}
}
这时候树的基本信息就被保存好了,接下来就是对树疯狂操作,修改查询什么的。现在的信息维护工作就通过DFS序交给线段树来处理了,把对链的查询修改操作转化成了区间修改查询操作。以查找最大值为例,视情况不同稍作修改。
int Query(int u, int v)
{
int res = -inf;
while(top[u] != top[v]){
if(depth[top[u]] < depth[top[v]]) swap(u, v);
res = max(res, query(1, 1, n, id[top[u]], id[u])); //线段树的查询函数
u = father[top[u]];
}
if(depth[u] < depth[v]) swap(u, v);
res = max(res, query(1, 1, n, id[v], id[u]));
return res;
}
几个题:
P2590 [ZJOI2008]树的统计——https://www.luogu.org/problemnew/show/P2590
单点修改、要维护最大值和区间和
#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn = 1e5+5;
const int inf = 0x7fffffff;
int head[maxn], nxt[maxn], to[maxn];
int n, a[maxn], sum[maxn<<2], Max[maxn<<2], q, cnt, Index;
int siz[maxn], heavy_son[maxn], father[maxn], depth[maxn], top[maxn], rk[maxn], id[maxn];
void addedge(int u, int v)
{
to[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
void dfs1(int u, int fa) ///size,heavy_son,depth,father
{
siz[u] = 1;
heavy_son[u] = 0;
for(int i=head[u]; i ; i=nxt[i]){
int v = to[i];
if(v == fa) continue;
depth[v] = depth[u] + 1;
father[v] = u;
dfs1(v, u);
siz[u] += siz[v];
if(siz[v] > siz[heavy_son[u]])
heavy_son[u] = v;
}
}
void dfs2(int u, int rt) ///id,rk,top
{
id[u] = ++Index;
rk[Index] = u;
top[u] = rt;
if(heavy_son[u]) dfs2(heavy_son[u], rt);
for(int i=head[u]; i; i=nxt[i]){
int v = to[i];
if(v == father[u] || v == heavy_son[u]) continue;
dfs2(v, v);
}
}
void pushup(int rt)
{
sum[rt] = sum[lson] + sum[rson];
Max[rt] = max(Max[lson], Max[rson]);
}
void build(int rt, int l, int r)
{
if(l == r){
sum[rt] = Max[rt] = a[rk[l]];
return ;
}
int mid = (l+r)>>1;
build(lson, l, mid);
build(rson, mid+1, r);
pushup(rt);
}
void update(int rt, int l, int r, int x, int val)
{
if(l == r){
sum[rt] = Max[rt] = val;
return ;
}
int mid = (l+r)>>1;
if(x <= mid) update(lson, l, mid, x, val);
else update(rson, mid+1, r, x, val);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R, bool flag)
{
if(L <= l && R >= r) return flag ? Max[rt] : sum[rt];
int mid = (l+r)>>1, ans;
if(flag){
ans = -inf;
if(L <= mid) ans = max(ans, query(lson, l, mid, L, R, flag));
if(R > mid) ans = max(ans, query(rson, mid+1, r, L, R, flag));
}else {
ans = 0;
if(L <= mid) ans += query(lson, l, mid, L, R, flag);
if(R > mid) ans += query(rson, mid+1, r, L, R, flag);
}
pushup(rt);
return ans ;
}
int Query(int u, int v, bool flag)
{
int res = flag ? -inf : 0;
while(top[u] != top[v]){
if(depth[top[u]] < depth[top[v]]) swap(u, v);
if(flag) res = max(res, query(1, 1, n, id[top[u]], id[u], flag));
else res += query(1, 1, n, id[top[u]], id[u], flag);
u = father[top[u]];
}
if(depth[u] < depth[v]) swap(u, v);
if(flag) res = max(res, query(1, 1, n, id[v], id[u], flag));
else res += query(1, 1, n, id[v], id[u], flag);
return res;
}
int main()
{
cin >> n;
int x, y;
for(int i=1; i<n; i++){
cin >> x >> y;
addedge(x, y);
addedge(y, x);
}
for(int i=1; i<=n; i++) cin >> a[i];
depth[1] = 1; father[1] = 1; dfs1(1, 0); dfs2(1, 1);
build(1, 1, n);
cin >> q;
for(int i=1; i<=q; i++){
string str;
cin >> str >> x >> y;
if(str[0] == 'C'){
update(1, 1, n, id[x], y);
}else if(str[1] == 'M'){
cout << Query(x, y, 1) << endl;
}else {
cout << Query(x, y, 0) << endl;
}
}
}
P3384 【模板】树链剖分——https://www.luogu.org/problemnew/show/P3384
多了区间修改,修改x的子树就是修改dfs序中区间 [id[x],id[x]+size[x]-1]
#include<bits/stdc++.h>
#define LL long long
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn = 5e5+5;
LL head[maxn], nxt[maxn], to[maxn];
LL n, a[maxn], sum[maxn<<2], q, m, lazy[maxn], r, p, cnt, Index;
LL siz[maxn], heavy_son[maxn], father[maxn], depth[maxn], top[maxn], rk[maxn], id[maxn];
void addedge(int u, int v)
{
to[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
void dfs1(int u, int fa) ///size,heavy_son,depth,father
{
siz[u] = 1;
heavy_son[u] = 0;
for(int i=head[u]; i ; i=nxt[i]){
int v = to[i];
if(v == fa) continue;
depth[v] = depth[u] + 1;
father[v] = u;
dfs1(v, u);
siz[u] += siz[v];
if(siz[v] > siz[heavy_son[u]])
heavy_son[u] = v;
}
}
void dfs2(int u, int rt) ///id,rk,top
{
id[u] = ++Index;
rk[Index] = u;
top[u] = rt;
if(heavy_son[u]) dfs2(heavy_son[u], rt);
for(int i=head[u]; i; i=nxt[i]){
int v = to[i];
if(v == father[u] || v == heavy_son[u]) continue;
dfs2(v, v);
}
}
void pushup(int rt)
{
sum[rt] = (sum[lson] + sum[rson])%p;
}
void pushdown(int rt, int len)
{
if(lazy[rt]!=0){
lazy[lson] = (lazy[lson] + lazy[rt])%p;
lazy[rson] = (lazy[rson] + lazy[rt])%p;
sum[lson] = (sum[lson] + lazy[rt]*(len-(len>>1)))%p;
sum[rson] = (sum[rson] + lazy[rt]*(len>>1))%p;
lazy[rt] = 0;
}
}
void build(int rt, int l, int r)
{
lazy[rt] = 0;
if(l == r){
sum[rt] = a[rk[l]]%p;
return ;
}
pushdown(rt, r-l+1);
int mid = (l+r)>>1;
build(lson, l, mid);
build(rson, mid+1, r);
pushup(rt);
}
void update(int rt, int l, int r, int L, int R, int val)
{
if(L <= l && R >= r) {
sum[rt] = (sum[rt]+val*(r-l+1))%p;
lazy[rt] = (lazy[rt] + val)%p;
return ;
}
pushdown(rt, r-l+1);
int mid = (l+r)>>1;
if(L <= mid) update(lson, l, mid, L, R, val);
if(R > mid) update(rson, mid+1, r, L, R, val);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R)
{
if(L <= l && R >= r) return sum[rt]%p ;
pushdown(rt, r-l+1);
int mid = l+r>>1; LL res=0;
if(L <= mid) res = (res + query(lson, l, mid, L, R))%p;
if(R > mid) res = (res + query(rson, mid+1, r, L, R))%p;
return res%p;
}
void Update(int u, int v, int val)
{
while(top[u] != top[v]){
if(depth[top[u]] < depth[top[v]]) swap(u, v);
update(1, 1, n, id[top[u]], id[u], val);
u = father[top[u]];
}
if(depth[u] < depth[v]) swap(u, v);
update(1, 1, n, id[v], id[u], val);
}
int Query(int u, int v)
{
LL res = 0;
while(top[u] != top[v]){
if(depth[top[u]] < depth[top[v]]) swap(u, v);
res = (res + query(1, 1, n, id[top[u]], id[u]))%p;
u = father[top[u]];
}
if(depth[u] < depth[v]) swap(u, v);
res = (res + query(1, 1, n, id[v], id[u]))%p;
return res%p;
}
int main()
{
cin >> n >> m >> r >> p;
for(int i=1; i<=n; i++) cin >> a[i];
for(int i=1; i<n; i++){
int x, y;
cin >> x >> y;
addedge(x, y); addedge(y, x);
}
depth[r] = 1; father[r] = r; dfs1(r, 0); dfs2(r, r);
build(1, 1, n);
for(int i=1; i<=m; i++){
int x, y, z, op;
cin >> op;
if(op == 1){
cin >> x >> y >> z;
Update(x, y, z);
}else if(op == 2){
cin >> x >> y;
cout << Query(x, y) << endl;
}else if(op == 3){
cin >> x >> z;
update(1, 1, n, id[x], id[x]+siz[x]-1, z);
}else {
cin >> x;
cout << query(1, 1, n, id[x], id[x]+siz[x]-1) << endl;
}
}
return 0;
}