树链剖分
引入
维护一棵树,支持两种操作
-
改变边权 | 边权
-
询问路径中最大权(或其他)
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;
}