树链剖分

引入

维护一棵树,支持两种操作

  • 改变边权 | 边权

  • 询问路径中最大权(或其他)


BF 的期望是 \(O(\log n)\),但是容易退化成 \(O(n)\),所以引入树链刨分,这里用轻重链刨分


轻重链刨分

\(SIZE_i\) 表示以 \(i\) 为根的子树的节点个数,那么对于 \(x\) 为的子节点 \(y\),若 \(SIZE_y\)\(x\) 子节点中最大的,则称改边为 ⌈ 重边 ⌋,否则为 ⌈ 轻边 ⌋

我们称全部由重边组成的路径为 ⌈ 重路径 ⌋

那么有如下性质

  • \((u,v)\) 是轻边,则 \(SIZE_v\leq SIZE_u/2\)

  • 从根到某一点的路径上轻边个数不大于 \(O(\log n)\)

  • 每个点到根的路径上不超过 \(O(\log n)\) 个重路径 & 轻边

对于询问,先处理出 \(LCA\),暴力处理轻边,数据结构维护重边


直接按思路写那可是要写不知道多少线段树,空间利用率低除非动态数组,查询起来还要把边分成各种奇奇怪怪得东西,总之非常麻烦,我们考虑将所有线段树合并,具体而言

  • 是按遍历顺序(重->轻)将原树重新排号,使得重链的点在区间上连续

这样方便维护,还保证了空间和常数


正常人维护都是先求 \(LCA\) 再修改,但也可以不用,常数的事

注意自己维护的是点权还是边权

复杂度是 \(O(\log^2 n)\)

#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)

using namespace std;

const int N=500010; 
int n, m, rt, mod, sp[N], id[N], idr[N], tot, top[N];
int dep[N], siz[N], son[N], fa[N], tr[N], tag[N];
vector<int> to[N];

inline void up(int p) {
	tr[p]=tr[ls(p)]+tr[rs(p)];
}

inline void down(int p,int s,int e) {
	if(!tag[p]) return;
	int mid=(s+e)>>1; 
	tr[ls(p)]+=tag[p]*(mid-s+1)%mod, tag[ls(p)]+=tag[p];
	tr[ls(p)]%=mod, tag[ls(p)]%=mod;
	tr[rs(p)]+=tag[p]*(e-mid)%mod, tag[rs(p)]+=tag[p];
	tr[rs(p)]%=mod, tag[rs(p)]%=mod;
	tag[p]=0;
}

void upt(int p,int s,int e,int l,int r,int val) {
	if(l<=s&&e<=r) {
		tr[p]+=val*(e-s+1)%mod, tag[p]+=val;
		tr[p]%=mod, tag[p]%=mod;
		return;
	}
	down(p,s,e); int mid=(s+e)>>1;
	if(l<=mid) upt(ls(p),s,mid,l,r,val);
	if(r>mid) upt(rs(p),mid+1,e,l,r,val);
	up(p); return;
}

int qur(int p,int s,int e,int l,int r) {
	if(l<=s&&e<=r) return tr[p];
	down(p,s,e); int mid=(s+e)>>1, sum=0;
	if(l<=mid) sum+=qur(ls(p),s,mid,l,r), sum%=mod;
	if(r>mid) sum+=qur(rs(p),mid+1,e,l,r), sum%=mod;
	up(p); return sum;
}

void dfs1(int x,int ft) {
	dep[x]=dep[ft]+1;
	siz[x]=1, fa[x]=ft;
	for(int y:to[x]) if(y^ft) {
		dfs1(y,x), siz[x]+=siz[y];
		if(!son[x]||siz[son[x]]<siz[y]) son[x]=y;
	}
}

void dfs2(int x,int ft) {
	id[x]=++tot, top[x]=ft;
	upt(1,1,n,id[x],id[x],sp[x]);
	if(son[x]) dfs2(son[x],ft);
	for(int y:to[x]) if(y^fa[x]&&y^son[x]) dfs2(y,y);
	idr[x]=tot; 
}

void addqwq(int x,int y,int val) {
	while(top[x]^top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		upt(1,1,n,id[top[x]],id[x],val);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	upt(1,1,n,id[x],id[y],val);
}

int qurqwq(int x,int y) {
	int res=0;
	while(top[x]^top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res+=qur(1,1,n,id[top[x]],id[x]);
		res%=mod, x=fa[top[x]];
	} 
	if(dep[x]>dep[y]) swap(x,y);
	res+=qur(1,1,n,id[x],id[y]);
	return res%mod;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m >> rt >> mod;
	for(int i=1; i<=n; ++i)
		cin >> sp[i];
	for(int i=1; i<n; ++i) {
		int u, v;
		cin >> u >> v;
		to[u].pb(v);
		to[v].pb(u);
	}
	dfs1(rt,0), dfs2(rt,rt);
	while(m--) {
		int op, x, y, z;
		cin >> op;
		if(op==1) {
			cin >> x >> y >> z;
			addqwq(x,y,z);
		}
		if(op==2) {
			cin >> x >> y;
			cout << qurqwq(x,y) << '\n';
		}
		if(op==3) {
			cin >> x >> z;
			upt(1,1,n,id[x],idr[x],z);
		}
		if(op==4) {
			cin >> x;
			cout << qur(1,1,n,id[x],idr[x]) << '\n';
		}
	}
 	return 0;
}
posted @ 2023-08-12 22:49  Hypoxia571  阅读(8)  评论(0编辑  收藏  举报