PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 P3384 【模板】轻重链剖分/树链剖分
题目
题目描述
如题,已知一棵包含 N N N 个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
-
1 x y z
,表示将树从 x x x 到 y y y 结点最短路径上所有节点的值都加上 z z z。 -
2 x y
,表示求树从 x x x 到 y y y 结点最短路径上所有节点的值之和。 -
3 x z
,表示将以 x x x 为根节点的子树内所有节点值都加上 z z z。 -
4 x
表示求以 x x x 为根节点的子树内所有节点值之和
输入格式
第一行包含 4 4 4 个正整数 N , M , R , P N,M,R,P N,M,R,P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。
接下来一行包含 N N N 个非负整数,分别依次表示各个节点上初始的数值。
接下来 N − 1 N-1 N−1 行每行包含两个整数 x , y x,y x,y,表示点 x x x 和点 y y y 之间连有一条边(保证无环且连通)。
接下来 M M M 行每行包含若干个正整数,每行表示一个操作。
输出格式
输出包含若干行,分别依次表示每个操作 2 2 2 或操作 4 4 4 所得的结果(对 P P P 取模)。
样例 #1
样例输入 #1
5 5 2 24
7 3 7 8 0
1 2
1 5
3 1
4 1
3 4 2
3 2 2
4 5
1 5 1 3
2 1 3
样例输出 #1
2
21
提示
【数据规模】
对于 30 % 30\% 30% 的数据: 1 ≤ N ≤ 10 1 \leq N \leq 10 1≤N≤10, 1 ≤ M ≤ 10 1 \leq M \leq 10 1≤M≤10;
对于 70 % 70\% 70% 的数据: 1 ≤ N ≤ 10 3 1 \leq N \leq {10}^3 1≤N≤103, 1 ≤ M ≤ 10 3 1 \leq M \leq {10}^3 1≤M≤103;
对于 100 % 100\% 100% 的数据: 1 ≤ N ≤ 10 5 1\le N \leq {10}^5 1≤N≤105, 1 ≤ M ≤ 10 5 1\le M \leq {10}^5 1≤M≤105, 1 ≤ R ≤ N 1\le R\le N 1≤R≤N, 1 ≤ P ≤ 2 31 − 1 1\le P \le 2^{31}-1 1≤P≤231−1。
【样例说明】
树的结构如下:
各个操作如下:
故输出应依次为 2 2 2 和 21 21 21。
题解
剖分
定义
重儿子:一个节点的子节点中,子树大小最大的子节点。
轻儿子:一个节点的子节点中,除重儿子以外的其他子节点。
重链:一个节点连接到它的重儿子的边。
轻链:一个节点连接到它的轻儿子的边。
数组
d
e
p
dep
dep:节点深度(根节点的深度为1)
s
o
n
son
son:重儿子的编号
s
i
z
e
size
size:子树的大小
f
a
fa
fa:父节点
d
f
n
dfn
dfn:节点的dfs序
r
n
k
rnk
rnk:dfs序对应的节点编号
t
o
p
top
top:一条链的链顶
预处理
dfs1
记录 d e p , s o n , s i z e , f a dep,son,size,fa dep,son,size,fa。
dfs2
记录
d
f
n
,
r
n
k
,
t
o
p
dfn,rnk,top
dfn,rnk,top。
注意要先搜每个节点的重儿子,这样可以保证每条链的
d
f
n
dfn
dfn都是连续的。
LCA
每次跳链顶深度较深的节点。
其他操作
基本上都是基于LCA或者子树大小进行的改动,虽然这道题本省不需要求LCA,但为了方便大家理解,我也把求LCA的函数放在代码里面了,大家可以对比查看。
线段树
就是一般的线段树的操作,注意要基于树的dfs序建线段树。
代码实现
100pts
//洛谷 P3384 【模板】轻重链剖分/树链剖分
#pragma GCC optimize(3)
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,r,p;
int x,y,z;
int cnt;
int opt;
int val[100010];
int b[400010];
int head[100010];
int son[100010];
int size[100010];
int dep[100010];
int fa[100010];
int top[100010];
int dfn[100010];
int rnk[100010];
int tag[400010];
struct tree{
int v,nex;
}a[200010];
void build1(int rt,int l,int r){
if(l==r){
b[rt]=val[rnk[l]];
return;
}
int mid=l+r>>1;
build1(rt<<1,l,mid);
build1(rt<<1|1,mid+1,r);
b[rt]=1ll*(b[rt<<1]+b[rt<<1|1])%p;
}
void build2(int u,int v){
a[++cnt].v=v;
a[cnt].nex=head[u];
head[u]=cnt;
}
void dfs1(int u){
son[u]=-1;
size[u]=1;
for(int i=head[u];i;i=a[i].nex){
int v=a[i].v;
if(!dep[v]){
dep[v]=dep[u]+1;
fa[v]=u;
dfs1(v);
size[u]+=size[v];
if(son[u]==-1||size[v]>size[son[u]]){
son[u]=v;
}
}
}
}
void dfs2(int u,int t){
top[u]=t;
dfn[u]=++cnt;
rnk[cnt]=u;
if(son[u]==-1){
return;
}
dfs2(son[u],t);
for(int i=head[u];i;i=a[i].nex){
if(a[i].v!=son[u]&&a[i].v!=fa[u]){
dfs2(a[i].v,a[i].v);
}
}
}
void push(int rt,int ll,int rr,int mid){
tag[rt<<1]=1ll*(tag[rt<<1]+tag[rt])%p;
tag[rt<<1|1]=1ll*(tag[rt<<1|1]+tag[rt])%p;
b[rt<<1]=1ll*(b[rt<<1]+1ll*tag[rt]*(mid-ll+1)%p)%p;
b[rt<<1|1]=1ll*(b[rt<<1|1]+1ll*tag[rt]*(rr-mid)%p)%p;
tag[rt]=0;
}
void change(int rt,int ll,int rr,int l,int r,int k){
if(ll>=l&&rr<=r){
tag[rt]+=k;
b[rt]+=k*(rr-ll+1);
return;
}
int mid=ll+rr>>1;
if(tag[rt]){
push(rt,ll,rr,mid);
}
if(l<=mid){
change(rt<<1,ll,mid,l,r,k);
}
if(r>mid){
change(rt<<1|1,mid+1,rr,l,r,k);
}
b[rt]=1ll*(b[rt<<1]+b[rt<<1|1])%p;
}
int query(int rt,int ll,int rr,int l,int r){
if(ll>=l&&rr<=r){
return b[rt];
}
int mid=ll+rr>>1;
if(tag[rt]){
push(rt,ll,rr,mid);
}
int ans=0;
if(l<=mid){
ans=1ll*(ans+query(rt<<1,ll,mid,l,r))%p;
}
if(r>mid){
ans=1ll*(ans+query(rt<<1|1,mid+1,rr,l,r))%p;
}
return ans%p;
}
int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
void change1(int x,int y,int z){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
}
change(1,1,n,dfn[top[x]],dfn[x],z);
x=fa[top[x]];
}
if(dep[x]>dep[y]){
swap(x,y);
}
change(1,1,n,dfn[x],dfn[y],z);
}
void change2(int x,int z){
change(1,1,n,dfn[x],dfn[x]+size[x]-1,z);
}
int query1(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]){
swap(x,y);
}
ans=1ll*(ans+query(1,1,n,dfn[top[x]],dfn[x]))%p;
x=fa[top[x]];
}
if(dep[x]>dep[y]){
swap(x,y);
}
ans=1ll*(ans+query(1,1,n,dfn[x],dfn[y]))%p;
return ans%p;
}
int query2(int x){
return query(1,1,n,dfn[x],dfn[x]+size[x]-1)%p;
}
int main(){
register int i;
scanf("%d%d%d%d",&n,&m,&r,&p);
for(i=1;i<=n;++i){
scanf("%d",&val[i]);
}
for(i=1;i<n;++i){
scanf("%d%d",&x,&y);
build2(x,y);
build2(y,x);
}
dep[r]=1;
dfs1(r);
cnt=0;
dfs2(r,r);
build1(1,1,n);
for(i=1;i<=m;++i){
scanf("%d",&opt);
if(opt==1){
scanf("%d%d%d",&x,&y,&z);
z%=p;
change1(x,y,z);
}
else if(opt==2){
scanf("%d%d",&x,&y);
printf("%d\n",query1(x,y));
}
else if(opt==3){
scanf("%d%d",&x,&z);
z%=p;
change2(x,z);
}
else{
scanf("%d",&x);
printf("%d\n",query2(x));
}
}
}