树链剖分
一、功能
- 将树从x到y结点最短路径上所有节点的值都加上z
- 求树从x到y结点最短路径上所有节点的值之和
- 将以x为根节点的子树内所有节点值都加上z
- 求以x为根节点的子树内所有节点值之和
二、概念
- 重儿子:对于每一个非叶子节点,它的儿子中 以那个儿子为根的子树节点数最大的儿子 为该节点的重儿子
- 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子
- 叶子节点没有重儿子也没有轻儿子
- 重边:一个父亲连接他的重儿子的边称为重边
- 轻边:剩下的即为轻边
- 重链:相邻重边连起来的 连接一条重儿子 的链叫重链
对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链。每一条重链以轻儿子为起点
三、预处理
dfs1
要处理几件事情:
- 标记每个点的深度dep[]
- 标记每个点的父亲fa[]
- 标记每个非叶子节点的子树大小(含它自己)
- 标记每个非叶子节点的重儿子编号son[]
dfs2
要预处理几件事情:
- 标记每个点的新编号
- 赋值每个点的初始值到新编号上
- 处理每个点所在链的顶端
- 处理每条链
顺序:先处理重儿子再处理轻儿子
四、树链剖分
用线段树来写
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; inline int read() { int sum = 0,p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } #define mid ((l + r) >> 1) #define len (r - l + 1) #define lson o << 1,l,mid #define rson o << 1 | 1,mid + 1,r const int maxn = 2e5 + 5,maxm = 1e5 + 5; int n,m,r,p; int val[maxn],dep[maxn],fa[maxn],siz[maxn],son[maxn],id[maxn],nval[maxn],top[maxn]; //val点权 ,dep点深度 ,fa父节点编号,siz子树大小,son重儿子,id新编号,nval新编号下的点权数,top当前链顶端结点 int res,tot,lazy[maxn * 4],a[maxn * 4]; int cnt,head[maxn]; struct edge { int nxt,to; }e[maxm * 4]; void add(int a,int b) { e[++cnt].nxt = head[a]; e[cnt].to = b; head[a] = cnt; } void dfs1(int o,int f,int deep)//o当前结点,f父节点,deep深度 { dep[o] = deep; fa[o] = f; siz[o] = 1; int maxson = -1; for(int i = head[o];i;i = e[i].nxt) { int v = e[i].to; if(v == f) continue; dfs1(v,o,deep+1); siz[o] += siz[v]; if(siz[v] > maxson) { son[o] = v; maxson = siz[v]; } } } void dfs2(int o,int topf) { id[o] = ++tot; nval[tot] = val[o]; top[o] = topf; if(!son[o]) return; dfs2(son[o],topf); for(int i = head[o];i;i = e[i].nxt) { int v = e[i].to; if(v == fa[o] || v == son[o]) continue; dfs2(v,v); } } //---------------线段树---------------- void pushdown(int o,int lenn) { lazy[o << 1] += lazy[o]; lazy[o << 1 | 1] += lazy[o]; a[o << 1] += lazy[o] * (lenn - (lenn >> 1)); a[o << 1 | 1] += lazy[o] * (lenn >> 1); a[o << 1] %= p; a[o << 1 | 1] %= p; lazy[o] = 0; } void build(int o,int l,int r) { if(l == r) { a[o] = nval[l] % p; return; } build(lson); build(rson); a[o] = (a[o << 1] + a[o << 1 | 1])% p; } void update(int o,int l,int r,int ql,int qr,int k) { if(ql <= l && r <= qr) { lazy[o] += k; a[o] += k * len; return; } if(lazy[o]) pushdown(o,len); if(ql <= mid) update(lson,ql,qr,k); if(qr > mid) update(rson,ql,qr,k); a[o] = (a[o << 1] + a[o << 1 | 1])%p; } void query(int o,int l,int r,int ql,int qr) { if(ql <= l && r <= qr) { res = (res +a[o])%p; return; } if(lazy[o]) pushdown(o,len); if(ql <= mid) query(lson,ql,qr); if(qr > mid) query(rson,ql,qr); } //---------------线段树—————————————— void updr(int l,int r,int k) { k %= p; while(top[l] != top[r]) { if(dep[top[l]] < dep[top[r]]) swap(l,r); update(1,1,n,id[top[l]],id[l],k); l = fa[top[l]]; } if(dep[l] > dep[r]) swap(l,r); update(1,1,n,id[l],id[r],k); } int queryr(int l,int r) { int ans = 0; while(top[l] != top[r]) { if(dep[top[l]] < dep[top[r]]) swap(l,r); res = 0; query(1,1,n,id[top[l]],id[l]); ans = (ans + res)%p; l = fa[top[l]]; } if(dep[l] > dep[r]) swap(l,r); res = 0; query(1,1,n,id[l],id[r]); return (ans + res) % p; } void updson(int o,int k) { update(1,1,n,id[o],id[o] + siz[o] - 1,k); } int qson(int o) { res = 0; query(1,1,n,id[o],id[o] + siz[o] - 1); return res; } int main() { n = read(),m = read(),r = read(),p = read(); for(int i = 1;i <= n;i++) val[i] = read(); for(int i = 1;i < n;i++) { int a = read(),b = read(); add(a,b); add(b,a); } dfs1(r,0,1); dfs2(r,r); build(1,1,n); while(m--) { int opt = read(); if(opt == 1) { int x = read(),y = read(),z = read(); updr(x,y,z); } else if(opt == 2) { int x = read(),y = read(); printf("%d\n",queryr(x,y)); } else if(opt == 3) { int x = read(),y = read(); updson(x,y); } else { int x = read(); printf("%d\n",qson(x)); } } return 0; }