树链剖分模板

本文详细介绍了树链剖分与线段树的数据结构实现,包括重儿子、轻儿子、重边、轻边的概念,以及如何通过树链剖分进行区间更新和查询操作。提供了完整的C++代码实现,涵盖了树链剖分的预处理、区间更新和查询等核心功能。

大佬博客:http://www.cnblogs.com/chinhhh/p/7965433.html#dfs1
模板题: https://www.luogu.org/problemnew/show/P3384

重儿子(非叶子结点):它某个点的作为儿子,且是儿子中子树结点数最多的儿子。
轻儿子(非叶子结点):非重儿子
重边:一个父亲连接他的重儿子的边称为重边
轻边:非重边
重链:

  1. 相邻重边连起来
  2. 连接一个重儿子
  3. 每个叶子结点,如果是轻儿子,则有一条以自己为起点的长度为1的链

把csdn当做云盘
模板

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
typedef pair<int,int> pii;

const int maxn = 2e5+5;
// 变量
int n,m,mod;
vector<int> G[maxn];
int w[maxn]; // 点权值
// 线段树
int val[maxn<<2], lazy[maxn<<2];
// 树链剖分
int son[maxn], id[maxn], fa[maxn],dep[maxn],siz[maxn],top[maxn],wt[maxn];
int cnt;
//------------------------------

// 线段树部分
void pushup(int rt) {
	val[rt] = (val[rt<<1]+val[rt<<1|1])%mod;
}
void pushdown(int rt, int l,int r) {
	if(lazy[rt]) {
		int mid = (l+r) >> 1;
		val[rt<<1] += lazy[rt]*(mid-l+1);
		val[rt<<1|1] += lazy[rt]*(r-mid);
		lazy[rt<<1] += lazy[rt];
		lazy[rt<<1|1] += lazy[rt];
		lazy[rt] = 0;
		val[rt<<1] %= mod;
		val[rt<<1|1] %= mod;
		lazy[rt<<1] %= mod;
		lazy[rt<<1|1] %= mod;
	}
}
void build(int l, int r,int rt) {
	if(l == r) {
		val[rt] = wt[l];
		return;
	}
	int mid = (l+r)>>1;
	build(lson);
	build(rson);
	pushup(rt);
}
void update(int L, int R, int k,int l, int r,int rt) {
	if(L > r || R < l) return;
	if(L <= l && r <= R) {
		val[rt] += k*(r-l+1);
		val[rt] %= mod;
		lazy[rt] += k;
		lazy[rt] %= mod;
		return;
	}
	int mid = (l+r)>>1;
	pushdown(rt,l,r);
	update(L,R,k,lson);
	update(L,R,k,rson);
	pushup(rt);
}
int query(int L, int R, int l,int r, int rt) {
	if(L > r || R < l) return 0;
	if(L <= l && r <= R) {
		return val[rt];
	}
	int mid = (l+r)>>1;
	int res = 0;
	pushdown(rt,l,r);
	res += query(L,R,lson);
	res %= mod;
	res += query(L,R,rson);
	res %= mod;
	pushup(rt);
	return res;
}
//------------------------------

//树链剖分,操作
// 求和
int qRange(int x,int y) {
	int ans = 0;
	while(top[x] != top[y]) { // 让其跑到一条链上
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		ans += query(id[top[x]],id[x],1,n,1);
		ans %= mod;
		x = fa[top[x]];
	}
	// 在同一条链上了
	if(dep[x] > dep[y]) swap(x,y);
	ans += query(id[x],id[y],1,n,1);
	ans %= mod;
	return ans;
}
// 更新
void updRange(int x,int y, int k) {
	k %= mod;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x,y);
		update(id[top[x]],id[x],k,1,n,1);
		x = fa[top[x]];
	}
	if(dep[x] > dep[y]) swap(x,y);
	update(id[x],id[y],k,1,n,1);
}
int qSon(int x) {
	return query(id[x],id[x]+siz[x]-1,1,n,1);
}
void updSon(int x, int k) {
	update(id[x],id[x]+siz[x]-1,k,1,n,1);
}
//------------------------------

// 树链剖分, 处理链
// u 当前结点,f父亲,deep深度
void dfs1(int u,int f,int deep) {
	dep[u] = deep;
	fa[u] = f;
	siz[u] = 1;
	int maxson=-1; // 重儿子子树大小
	for(int i = 0; i < G[u].size(); ++i) {
		int v = G[u][i];
		if(v == f) continue;
		dfs1(v,u,deep+1);
		siz[u] += siz[v];
		if(siz[v] > maxson) {
			son[u] = v;
			maxson = siz[v];
		}
	}
}
// u 当前结点,topf当前链的最顶端的节点
void dfs2(int u, int topf) {
	id[u] = ++cnt; // u点编号为cnt
	wt[cnt] = w[u]; // 记录新编号的权值
	top[u] = topf; // 该点所在链的链头
	if(!son[u]) return;
	dfs2(son[u], topf); // 先处理重儿子
	for(int i = 0; i < G[u].size(); ++i) {
		int v = G[u][i];
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v,v); // 每个轻儿子都有从自己开始的链
	}
}
//------------------------------

int main() {
	int q,rt;
	scanf("%d%d%d%d", &n, &q, &rt, &mod);
	for(int i = 1; i <= n; ++i)
		scanf("%d", &w[i]);
	int u,v;
	for(int i = 0; i < n-1; ++i) {
		scanf("%d%d", &u, &v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs1(rt,-1,1);
	dfs2(rt,rt);
	build(1,n,1);
	int opt,x,y,z;
	for(int i = 0; i < q; ++i) {
		scanf("%d", &opt);
		if(opt == 1) {
			scanf("%d%d%d", &x, &y, &z);
			updRange(x,y,z);
		} else if(opt == 2) {
			scanf("%d%d", &x, &y);
			printf("%d\n", qRange(x,y));
		} else if(opt == 3) {
			scanf("%d%d", &x, &z);
			updSon(x,z);
		} else if(opt == 4) {
			scanf("%d", &x);
			printf("%d\n", qSon(x));
		}
	}
	return 0;
}
#include <cstdio> #include <iostream> #include <vector> #define N 30003 #define INF 2147483647 using namespace std; int n,f[N][20],dep[N],siz[N],son[N],top[N],tot,pos[N],w[N]; int Max[N*4],Sum[N*4]; vector <int> to[N]; void dfs1(int x){ siz[x]=1; int sz=to[x].size(); for(int i=0;i<sz;++i){ int y=to[x][i]; if(y==f[x][0])continue; f[y][0]=x; dep[y]=dep[x]+1; dfs1(y); siz[x]+=siz[y]; if(siz[y]>siz[son[x]])son[x]=y; } } void dfs2(int x,int root){ top[x]=root; pos[x]=++tot; if(son[x])dfs2(son[x],root); int sz=to[x].size(); for(int i=0;i<sz;++i){ int y=to[x][i]; if(y==f[x][0] || y==son[x])continue; dfs2(y,y); } } void update(int k,int l,int r,int P,int V){ if(l==r){ Max[k]=Sum[k]=V; return; } int mid=(l+r)>>1; if(P<=mid)update(k*2,l,mid,P,V); else update(k*2+1,mid+1,r,P,V); Max[k]=max(Max[k*2],Max[k*2+1]); Sum[k]=Sum[k*2]+Sum[k*2+1]; } void up(int &x,int goal){ for(int i=15;i>=0;--i) if(dep[f[x][i]]>=goal)x=f[x][i]; } int lca(int x,int y){ if(dep[x]>dep[y])up(x,dep[y]); if(dep[x]<dep[y])up(y,dep[x]); if(x==y)return x; for(int i=15;i>=0;--i) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } int getm(int k,int l,int r,int L,int R){ if(L<=l && r<=R)return Max[k]; int res=-INF,mid=(l+r)>>1; if(L<=mid)res=max(res,getm(k*2,l,mid,L,R)); if(R>mid)res=max(res,getm(k*2+1,mid+1,r,L,R)); return res; } int gets(int k,int l,int r,int L,int R){ if(L<=l && r<=R)return Sum[k]; int res=0,mid=(l+r)>>1; if(L<=mid)res+=gets(k*2,l,mid,L,R); if(R>mid)res+=gets(k*2+1,mid+1,r,L,R); return res; } int main(){ scanf("%d",&n); for(int i=1,a,b;i<n;++i){ scanf("%d%d",&a,&b); to[a].push_back(b); to[b].push_back(a); } dep[1]=1; dfs1(1); dfs2(1,1); for(int i=1;i<=15;++i) for(int j=1;j<=n;++j)f[j][i]=f[f[j][i-1]][i-1]; for(int i=1;i<=n;++i){ scanf("%d",&w[i]); update(1,1,n,pos[i],w[i]); } int q; scanf("%d",&q); while(q--){ char s[10]; int u,v,t; scanf("%s",s); if(s[1]=='H'){ scanf("%d%d",&u,&t); w[u]=t; update(1,1,n,pos[u],t); } if(s[1]=='M'){ scanf("%d%d",&u,&v); int ans=-INF,t=lca(u,v); for(int i=u;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans=max(ans,getm(1,1,n,pos[top[i]],pos[i])); else{ ans=max(ans,getm(1,1,n,pos[t],pos[i])); break; } for(int i=v;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans=max(ans,getm(1,1,n,pos[top[i]],pos[i])); else{ ans=max(ans,getm(1,1,n,pos[t],pos[i])); break; } printf("%d\n",ans); } if(s[1]=='S'){ scanf("%d%d",&u,&v); int ans=0,t=lca(u,v); for(int i=u;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans+=gets(1,1,n,pos[top[i]],pos[i]); else{ ans+=gets(1,1,n,pos[t],pos[i]); break; } for(int i=v;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans+=gets(1,1,n,pos[top[i]],pos[i]); else{ ans+=gets(1,1,n,pos[t],pos[i]); break; } printf("%d\n",ans-w[t]); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值