树链剖分

树链剖分

前言

重剖:从去年省选鸽到现在,其实更准确的是从初二下学期第一次学,一直鸽到现在。

笑点解析:重剖:我叫 tree_pou_heavy(重的树剖)

简介

树链剖分就是把树按照某种规定剖成若干条链,便于维护修改和查询。目前我知道的有重链剖分、长链剖分、实链剖分(又称 LCT)。

重链剖分

重链剖分可以维护树上链加、链查、子树加、子树查。基本上有链操作的都可以考虑下重剖(和长剖?)。

每个节点,子树大小(指节点个数)最大的儿子,称为重儿子,其他儿子叫做轻儿子。每个节点和其重儿子连边叫做重边,和其轻儿子连边叫做轻边。把轻边断开,树就被分成了若干条(最多 \(O(n)\) 条)链(一个节点也算一条链)。

我们按照先遍历重儿子,再遍历轻儿子的访问顺序钦定 dfs 序,这样可以保证一条链的 dfs 序是连续的。

我们使用数据结构维护每条链的信息,常见的有线段树维护。

\(u,v\) 之间的链,可以划分成若干条树链,我们对每条树链分别进行区间加、区间查询操作即可。对每个节点记录一个 \(top_u\) 表示点 \(u\) 所在的链,深度最小的点是什么。方便跳链。

如果对每条链单独开一棵线段树维护,常数应该会较小,但是如果题目还有子树操作,就必须维护全局的线段树了。

证明点 \(u,v\) 之间有 \(O(\log n)\) 条树链:我们考虑 \(u,lca\) 之间有多少树链。也就是数 \(u,lca\) 之间有多少条轻边。每经过一条轻边从 \(x\) 到达 \(fa_x\),说明 \(fa_x\) 一定有一个重儿子 \(y\) 满足 \(siz_y \ge sizx\),因此 \(siz_{fa_x} \ge 2siz_x\),子树大小会翻倍,而最多翻 \(\log n\) 倍。因此这样的轻边只有 \(O(\log n)\) 条。证毕。

加上线段树复杂度,时间复杂度是 \(O(n \log^2 n)\)。重剖常数是较小的,因为你一般跳不完 \(\log n\) 条链。只是线段树常数较大。但是我们有可爱的 zkw 小常数线段树。

综上,重剖预处理 \(O(n)\),单次操作 \(O(\log)\) 乘上每条链操作时间复杂度。优点是比较板,需要上树剖的题目一般维护的操作比较雷同(当然你需要先推出操作)。缺点是码量较大。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace tree_pou_heavy {
	constexpr int N=1e5+7;
	int n,m,rt,mod;
	int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
	void _add(int &a,int b) { a=add(a,b); }
	void _max(int &a,int b) { a=max(a,b); }
	int a[N];
	int u,v;
	struct edge {
		int to,ne;
	}e[N<<1];
	int head[N],e_cnt;
	void addedge(int u,int v) { e[++e_cnt] = {v, head[u]}; head[u]=e_cnt; }
	struct tree {
		int fa[N],siz[N],gson[N],dep[N];
		int dfn[N],eddfn[N],cnt,idfn[N];
		int top[N];
		void dfs(int u,int f) {
			fa[u]=f;
			dep[u]=dep[f]+1;
			siz[u]=1;
			for(int i=head[u]; i; i=e[i].ne) {
				int v=e[i].to;
				if(v==f) continue;
				dfs(v,u);
				siz[u]+=siz[v];
				if(siz[v]>siz[gson[u]]) gson[u]=v;
			}
		}
		void dfs(int u) {
			dfn[u]=eddfn[u]=++cnt;
			idfn[cnt]=u;
			if(gson[u]) top[gson[u]]=top[u], dfs(gson[u]);
			for(int i=head[u]; i; i=e[i].ne) {
				int v=e[i].to;
				if(v==fa[u] || v==gson[u]) continue;
				top[v]=v, dfs(v);
			}
			eddfn[u]=cnt;
		}
		int tr[N<<2],tag[N<<2];
		int p;
		void build() {
			p=1;
			while(p-2<n) p<<=1;
			rep(i,1,n) tr[p+i]=a[idfn[i]];
			per(i,p-1,1) tr[i]=add(tr[i<<1],tr[i<<1|1]);
		}
		void init() {
			dfs(rt,0);
			top[rt]=rt;
			dfs(rt);
			build();
		}
		void maketag(int u,int x,int siz) { _add(tr[u],1ll*x*siz%mod), _add(tag[u],x); }
		void pushup(int u,int siz) { tr[u]=add(add(tr[u<<1],tr[u<<1|1]),1ll*siz*tag[u]%mod); }
		void _update(int l,int r,int x) {
			l=p+l-1, r=p+r+1;
			int siz=1;
			while(l^r^1) {
				if((l&1)^1) maketag(l^1,x,siz);
				if(r&1) maketag(r^1,x,siz);
				l>>=1, r>>=1, siz<<=1;
				pushup(l,siz), pushup(r,siz);
			} 
			for(l>>=1, siz<<=1; l; l>>=1, siz<<=1) pushup(l,siz);
		}
		int _query(int l,int r) {
			l=p+l-1, r=p+r+1;
			int sizl=0,sizr=0,siz=1;
			int s=0;
			while(l^r^1) {
				if((l&1)^1) _add(s,tr[l^1]), sizl+=siz;
				if(r&1) _add(s,tr[r^1]), sizr+=siz;
				l>>=1, r>>=1, siz<<=1;
				_add(s,1ll*tag[l]*sizl%mod), _add(s,1ll*tag[r]*sizr%mod);
			}
			for(l>>=1, sizl+=sizr; l; l>>=1) _add(s,1ll*tag[l]*sizl%mod);
			return s;
		}
		void update(int u,int v,int x) {
			while(top[u]^top[v]) {
				if(dep[top[u]]<dep[top[v]]) swap(u,v);
				_update(dfn[top[u]],dfn[u],x), u=fa[top[u]];
			}
			if(dfn[u]>dfn[v]) swap(u,v);
			_update(dfn[u],dfn[v],x);
		}
		void update(int u,int x) {
			_update(dfn[u],eddfn[u],x);
		}
		int query(int u,int v) {
			int s=0;
			while(top[u]^top[v]) {
				if(dep[top[u]]<dep[top[v]]) swap(u,v);
				_add(s,_query(dfn[top[u]],dfn[u])), u=fa[top[u]];
			}
			if(dfn[u]>dfn[v]) swap(u,v);
			_add(s,_query(dfn[u],dfn[v]));
			return s;
		}
		int query(int u) {
			return _query(dfn[u],eddfn[u]);
		}
	}T;
	int op,x;
	void main() {
		sf("%d%d%d%d",&n,&m,&rt,&mod);
		rep(i,1,n) sf("%d",&a[i]), a[i]%=mod;
		rep(i,1,n-1) sf("%d%d",&u,&v), addedge(u,v), addedge(v,u);
		T.init();
		rep(i,1,m) {
			sf("%d",&op);
			if(op==1) sf("%d%d%d",&u,&v,&x), T.update(u,v,x);
			else if(op==2) sf("%d%d",&u,&v), pf("%d\n",T.query(u,v));
			else if(op==3) sf("%d%d",&u,&x), T.update(u,x);
			else sf("%d",&u), pf("%d\n",T.query(u));
		}
	}
}
int main() {
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	#endif
	tree_pou_heavy :: main();
}
posted @ 2024-11-26 17:01  liyixin  阅读(11)  评论(0编辑  收藏  举报