树链剖分

博客围绕树链剖分展开,给出模板,并列举洛谷的相关题目。包括模板题 P3384,例题 P2146 软件包管理器涉及根节点到指定节点路径和、子树节点权值和计算,例题 P2590 是树的统计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

名称解释
f [ u ] f[u] f[u]保存结点 u u u 的父亲节点
d e p [ u ] dep[u] dep[u]保存结点 u u u 的深度值
s i z e [ u ] size[u] size[u]保存以 u u u 为根的子树节点个数
s o n [ u ] son[u] son[u]保存重儿子
r k [ u ] rk[u] rk[u]保存当前 d f s dfs dfs 标号在树中所对应的节点
t o p [ u ] top[u] top[u]保存当前节点所在链的顶端节点(替代 l c a lca lca
i d [ u ] id[u] id[u]保存树中每个节点剖分以后的新编号( D F S DFS DFS的执行顺序)

模板:

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
ll mod = 1e9 + 7;
const int maxn = 3e5 + 10;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pushup(rt) t[rt] = t[rt<<1] + t[rt<<1|1];
ll t[maxn<<2], lazy[maxn<<2], a[maxn];

int n, m, f[maxn], size[maxn];
int cnt, head[maxn], dep[maxn]; 
int rk[maxn], rks, id[maxn];
int son[maxn], top[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

/*-------------------------树剖-------------------------*/
void dfs1(int cur, int fa, int de){
	dep[cur] = de, f[cur] = fa, size[cur] = 1;
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == fa) continue;
		dfs1(v, cur, de+1);
		size[cur] += size[v];
		if(size[son[cur]] < size[v]) son[cur] = v;
	}
}

void dfs2(int cur, int tp){
	top[cur] = tp, id[cur] = ++rks, rk[rks] = cur;
	if(son[cur]) dfs2(son[cur], tp);
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == f[cur]) continue;
		if(v != son[cur]) dfs2(v, v);
	}
}
/*-------------------------树剖-------------------------*/

/*-------------------------线段树-------------------------*/
void build(int l,int r,int rt){
	lazy[rt] = 0;
	if(l==r){
		t[rt] = a[rk[l]];
		return ;
	}
	int m = l + r >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}

void pushdown(int l,int r,int rt) {
	if(lazy[rt]) {
		lazy[rt<<1] += lazy[rt];
		lazy[rt<<1|1] += lazy[rt];
		t[rt<<1] += l*lazy[rt];
		t[rt<<1|1] += r*lazy[rt];
		lazy[rt] = 0;
	}
}

void update(int L,int R,int C,int l,int r,int rt) {
	if(L<=l&&r<=R) {
		t[rt] += (r-l+1)*C;
		lazy[rt] += C;
		return ;
	}
	int m = (l+r)>>1;
	pushdown(m-l+1,r-m,rt);
	if(L<=m) update(L,R,C,lson);
	if(R>m) update(L,R,C,rson);
	pushup(rt);
}

ll query(int L,int R,int l,int r,int rt) {
	if(L<=l&&r<=R) return t[rt];
	int m = (l+r)>>1;
	pushdown(m-l+1,r-m,rt);
	ll ans = 0;
	if(L<=m) ans += query(L,R,lson);
	if(R>m) ans += query(L,R,rson);
	return ans;
}
/*-------------------------线段树-------------------------*/

/*----------------------树剖 + 线段树----------------------*/
ll sum(int x, int y){
	ll ret = 0;
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		ret += query(id[top[x]],id[x],1,n,1);
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	return ret + query(id[x],id[y],1,n,1);
}

void updates(int x, int y, int c){
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		update(id[top[x]],id[x],c,1,n,1);
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	update(id[x],id[y],c,1,n,1);
}
/*----------------------树剖 + 线段树----------------------*/

int main() {
	int rt;
	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 u, v;
		scanf("%d%d", &u, &v);
		add(u, v, 1);
		add(v, u, 1);
	}
	dfs1(rt, 0, 1);
	dfs2(rt, rt);
	build(1, n, 1);
	for(int i=1; i<=m; i++){
		int x, y, c, op;
		scanf("%d", &op);
		if(op == 1){
			scanf("%d%d%d", &x, &y, &c);
			updates(x, y, c);
		}
		else if(op == 2){
			scanf("%d%d", &x, &y);
			printf("%lld\n", sum(x, y));
		}		
		else if(op == 3){
			scanf("%d%d", &x, &c);
			update(id[x],id[x]+size[x]-1,c,1,n,1);
		}
		else {
			scanf("%d", &x);
			printf("%lld\n", query(id[x],id[x]+size[x]-1,1,n,1));
		}
	}
}

模板题:洛谷 P3384

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
ll mod = 1e9 + 7;
const int maxn = 3e5 + 10;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pushup(rt) t[rt] = (t[rt<<1] + t[rt<<1|1]) % mod;
ll t[maxn<<2], lazy[maxn<<2], a[maxn];

int n, m, f[maxn], size[maxn];
int cnt, head[maxn], dep[maxn]; 
int rk[maxn], rks, id[maxn];
int son[maxn], top[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

/*-------------------------树剖-------------------------*/
void dfs1(int cur, int fa, int de){
	dep[cur] = de, f[cur] = fa, size[cur] = 1;
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == fa) continue;
		dfs1(v, cur, de+1);
		size[cur] += size[v];
		if(size[son[cur]] < size[v]) son[cur] = v;
	}
}

void dfs2(int cur, int tp){
	top[cur] = tp, id[cur] = ++rks, rk[rks] = cur;
	if(son[cur]) dfs2(son[cur], tp);
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == f[cur]) continue;
		if(v != son[cur]) dfs2(v, v);
	}
}
/*-------------------------树剖-------------------------*/

/*-------------------------线段树-------------------------*/
void build(int l,int r,int rt){
	lazy[rt] = 0;
	if(l==r){
		t[rt] = a[rk[l]];
		return ;
	}
	int m = l + r >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}

void pushdown(int l,int r,int rt) {
	if(lazy[rt]) {
		(lazy[rt<<1] += lazy[rt]) %= mod;
		(lazy[rt<<1|1] += lazy[rt]) %= mod;
		(t[rt<<1] += l*lazy[rt]) %= mod;
		(t[rt<<1|1] += r*lazy[rt]) %= mod;
		lazy[rt] = 0;
	}
}

void update(int L,int R,int C,int l,int r,int rt) {
	if(L<=l&&r<=R) {
		(t[rt] += (r-l+1)*C) %= mod;
		(lazy[rt] += C) %= mod;
		return ;
	}
	int m = (l+r)>>1;
	pushdown(m-l+1,r-m,rt);
	if(L<=m) update(L,R,C,lson);
	if(R>m) update(L,R,C,rson);
	pushup(rt);
}

int query(int L,int R,int l,int r,int rt) {
	if(L<=l&&r<=R) return t[rt];
	int m = (l+r)>>1;
	pushdown(m-l+1,r-m,rt);
	ll ans = 0;
	if(L<=m) ans += query(L,R,lson);
	if(R>m) ans += query(L,R,rson);
	return ans % mod;
}
/*-------------------------线段树-------------------------*/

/*----------------------树剖 + 线段树----------------------*/
int sum(int x, int y){
	int ret = 0;
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		(ret += query(id[top[x]],id[x],1,n,1)) %= mod;
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	return (ret + query(id[x],id[y],1,n,1)) % mod;
}

void updates(int x, int y, int c){
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		update(id[top[x]],id[x],c,1,n,1);
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	update(id[x],id[y],c,1,n,1);
}
/*----------------------树剖 + 线段树----------------------*/

int main() {
	int rt;
	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 u, v;
		scanf("%d%d", &u, &v);
		add(u, v, 1);
		add(v, u, 1);
	}
	dfs1(rt, 0, 1);
	dfs2(rt, rt);
	build(1, n, 1);
	for(int i=1; i<=m; i++){
		int x, y, c, op;
		scanf("%d", &op);
		if(op == 1){
			scanf("%d%d%d", &x, &y, &c);
			updates(x, y, c);
		}
		else if(op == 2){
			scanf("%d%d", &x, &y);
			printf("%d\n", sum(x, y));
		}		
		else if(op == 3){
			scanf("%d%d", &x, &c);
			update(id[x],id[x]+size[x]-1,c,1,n,1);
		}
		else {
			scanf("%d", &x);
			printf("%d\n", query(id[x],id[x]+size[x]-1,1,n,1));
		}
	}
}

例题一:洛谷 P2146 软件包管理器

安装软件包 x x x:计算从根节点到 x x x 的路径和
卸载软件包 x x x:计算 x x x 的所有子树节点权值和

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
ll mod = 1e9 + 7;
const int maxn = 3e5 + 10;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pushup(rt) t[rt] = t[rt<<1] + t[rt<<1|1];
ll t[maxn<<2], lazy[maxn<<2], a[maxn];

int n, m, f[maxn], size[maxn];
int cnt, head[maxn], dep[maxn]; 
int rk[maxn], rks, id[maxn];
int son[maxn], top[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

/*-------------------------树剖-------------------------*/
void dfs1(int cur, int fa, int de){
	dep[cur] = de, f[cur] = fa, size[cur] = 1;
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == fa) continue;
		dfs1(v, cur, de+1);
		size[cur] += size[v];
		if(size[son[cur]] < size[v]) son[cur] = v;
	}
}

void dfs2(int cur, int tp){
	top[cur] = tp, id[cur] = ++rks, rk[rks] = cur;
	if(son[cur]) dfs2(son[cur], tp);
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == f[cur]) continue;
		if(v != son[cur]) dfs2(v, v);
	}
}
/*-------------------------树剖-------------------------*/

/*-------------------------线段树-------------------------*/
void build(int l,int r,int rt){
	lazy[rt] = -1;
	if(l==r){
		t[rt] = 0;
		return ;
	}
	int m = l + r >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}

void pushdown(int l,int r,int rt) {
	if(lazy[rt]!=-1) {
		lazy[rt<<1] = lazy[rt];
		lazy[rt<<1|1] = lazy[rt];
		t[rt<<1] = l*lazy[rt];
		t[rt<<1|1] = r*lazy[rt];
		lazy[rt] = -1;
	}
}

void update(int L,int R,int C,int l,int r,int rt) {
	if(L<=l&&r<=R) {
		t[rt] = (r-l+1)*C;
		lazy[rt] = C;
		return ;
	}
	int m = (l+r)>>1;
	pushdown(m-l+1,r-m,rt);
	if(L<=m) update(L,R,C,lson);
	if(R>m) update(L,R,C,rson);
	pushup(rt);
}

int query(int L,int R,int l,int r,int rt) {
	if(L<=l&&r<=R) return t[rt];
	int m = (l+r)>>1;
	pushdown(m-l+1,r-m,rt);
	ll ans = 0;
	if(L<=m) ans += query(L,R,lson);
	if(R>m) ans += query(L,R,rson);
	return ans;
}
/*-------------------------线段树-------------------------*/

/*----------------------树剖 + 线段树----------------------*/
void updates(int x, int y, int c){
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		update(id[top[x]],id[x],c,1,n,1);
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	update(id[x],id[y],c,1,n,1);
}
/*----------------------树剖 + 线段树----------------------*/

int main() {
	scanf("%d", &n);
	for(int i=2; i<=n; i++){
		int v;
		scanf("%d", &v);
		v++;
		add(i, v, 1);
		add(v, i, 1);
	}
	dfs1(1, 0, 1);
	dfs2(1, 1);
	build(1, n, 1);
	scanf("%d", &m);
	for(int i=1; i<=m; i++){
		int x;
		char op[20];
		int t1 = t[1];
		scanf("%s%d", op, &x);
		x++;
		if(op[0] == 'i'){
			updates(1, x, 1);
			printf("%d\n", t[1] - t1);
		}
		else {
			printf("%d\n", query(id[x],id[x]+size[x]-1,1,n,1));
			update(id[x],id[x]+size[x]-1,0,1,n,1);
		}	
	}
}

例题二:洛谷 P2590 树的统计

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 3e5 + 10;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
ll t1[maxn<<2], t2[maxn<<2], a[maxn];

int n, m, f[maxn], size[maxn];
int cnt, head[maxn], dep[maxn]; 
int rk[maxn], rks, id[maxn];
int son[maxn], top[maxn];

struct EDGE {
	int next, to, w;
} edge[maxn<<2];

void add(int u, int v, int w) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].w = w;
	head[u] = cnt;
}

/*-------------------------树剖-------------------------*/
void dfs1(int cur, int fa, int de){
	dep[cur] = de, f[cur] = fa, size[cur] = 1;
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == fa) continue;
		dfs1(v, cur, de+1);
		size[cur] += size[v];
		if(size[son[cur]] < size[v]) son[cur] = v;
	}
}

void dfs2(int cur, int tp){
	top[cur] = tp, id[cur] = ++rks, rk[rks] = cur;
	if(son[cur]) dfs2(son[cur], tp);
	for(int i=head[cur]; i; i=edge[i].next){
		int v = edge[i].to;
		if(v == f[cur]) continue;
		if(v != son[cur]) dfs2(v, v);
	}
}
/*-------------------------树剖-------------------------*/

/*-------------------------线段树-------------------------*/
void pushup(int rt){
	t1[rt] = t1[rt<<1] + t1[rt<<1|1];
	t2[rt] = max(t2[rt<<1], t2[rt<<1|1]);
} 

void build(int l,int r,int rt){
	if(l==r){
		t1[rt] = t2[rt] = a[rk[l]];
		return ;
	}
	int m = l + r >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}

void update(int L,int R,int C,int l,int r,int rt) {
	if(L<=l&&r<=R) {
		t1[rt] = (r-l+1)*C;
		t2[rt] = C;
		return ;
	}
	int m = (l+r)>>1;
	if(L<=m) update(L,R,C,lson);
	if(R>m) update(L,R,C,rson);
	pushup(rt);
}

ll query1(int L,int R,int l,int r,int rt) {
	if(L<=l&&r<=R) return t2[rt];
	int m = (l+r)>>1;
	ll ans = -1e10;
	if(L<=m) ans = max(ans, query1(L,R,lson));
	if(R>m) ans = max(ans, query1(L,R,rson));
	return ans;
}

ll query2(int L,int R,int l,int r,int rt) {
	if(L<=l&&r<=R) return t1[rt];
	int m = (l+r)>>1;
	ll ans = 0;
	if(L<=m) ans += query2(L,R,lson);
	if(R>m) ans += query2(L,R,rson);
	return ans;
}
/*-------------------------线段树-------------------------*/

/*----------------------树剖 + 线段树----------------------*/
ll sum1(int x, int y){
	ll ret = -1e10;
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		ret = max(ret, query1(id[top[x]],id[x],1,n,1));
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	return max(ret, query1(id[x],id[y],1,n,1));
}

ll sum2(int x, int y){
	ll ret = 0;
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		ret += query2(id[top[x]],id[x],1,n,1);
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	return ret + query2(id[x],id[y],1,n,1);
}

void updates(int x, int y, int c){
	while(top[x] ^ top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x, y);
		update(id[top[x]],id[x],c,1,n,1);
		x = f[top[x]];
	}
	if(id[x]>id[y]) swap(x, y);
	update(id[x],id[y],c,1,n,1);
}
/*----------------------树剖 + 线段树----------------------*/

int main() {
	scanf("%d", &n);
	for(int i=1; i<n; i++){
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v, 1);
		add(v, u, 1);
	}
	for(int i=1; i<=n; i++) scanf("%lld", a+i);
	dfs1(1, 0, 1);
	dfs2(1, 1);
	build(1, n, 1);
	scanf("%d", &m);
	for(int i=1; i<=m; i++){
		char s[10];
		int x, y;
		scanf("%s%d%d", s, &x, &y);
		if(s[1] == 'H') updates(x, x, y);
		else if(s[1] == 'M') printf("%lld\n", sum1(x, y));
		else if(s[1] == 'S') printf("%lld\n", sum2(x, y));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值