P3384 【模板】轻重链剖分/树链剖分P3384 【模板】轻重链剖分/树链剖分
树链剖分
可能要树剖的题目特点:对树上路径进行操作,对子树进行操作。
本题需要对树上路径进行求和改值,对子树进行求和与改值操作。因此使用树剖+线段树来进行
线段树直接板子无修改,注意建树时用的权值是经过dfs序排序后的权值。
在建树后我们需要进行两次树上dfs,这两次dfs有着不同的目的,且需要注意先后顺序
dfs1:第一次dfs需要将所有一次dfs可以查出的数据查出,深度,节点的父亲可以记录,同时子树的大小也可以求出,最后轻重链也可以划分出来 ,即儿子中子树最大的为当前节点的重儿子节点。
dfs2:一条重链可以由每个节点的重儿子连接,但是其顶点节点需要我们记录,当一个节点没有重儿子时就表明走到头了,return。同时注意我们要记录每个节点的id,即节点与dfs序的关系,同时注意为方便建立线段树,我们要将现在的节点权值用dfs序排列,wt【i】=w【x】。
在更新范围节点时,我们可以将深度较深的节点在两端点不在(o´ω`o)同一重链时直接更新一整条重链,然后把节点向顶点的父亲更新,然后再判断,当到达同一重链时直接更新两个端点,注意缺点好两点的深度。(范围查询同理)
在更新节点子树时,只需要更新节点到它子树大小-1即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include <stack>
#include<map>
#include<stdlib.h>
#include<queue>
#define LL long long
using namespace std;
const int N=2e5+10,M=1e4+10,inf=0x3f3f3f3f;
const LL INF=1e18;
int n,m,r,mod;
int ecnt,head[N],nxt[N],to[N],w[N],wt[N];
int atre[N<<2],lazy[N<<2];
int son[N],dep[N],fa[N],cnt,id[N],top[N],siz[N];
int res=0;
void addtre(int x,int y) {
to[++ecnt]=y;
nxt[ecnt]=head[x];
head[x]=ecnt;
}
void pushdown(int rt,int l,int r) {
int mid=l+((r-l)>>1);
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
atre[rt<<1]+=1LL*lazy[rt]*(mid-l+1);
atre[rt<<1|1]+=1LL*lazy[rt]*(r-mid);
atre[rt<<1]%=mod;
atre[rt<<1|1]%=mod;
lazy[rt]=0;
}
void pushup(int rt) {
atre[rt]=(atre[rt<<1]+atre[rt<<1|1])%mod;
}
void build(int rt,int l,int r) {
if(r==l) {
atre[rt]=wt[l];
return;
}
int mid=l+((r-l)>>1);
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R) {
if(L<=l&&r<=R) {
return atre[rt];
}
LL sum=0;
int mid=l+((r-l)>>1);
if(lazy[rt])pushdown(rt,l,r);
if(L<=mid)sum+=query(rt<<1,l,mid,L,R);
sum%=mod;
if(R>mid)sum+=query(rt<<1|1,mid+1,r,L,R);
return sum%mod;
}
void update(int rt,int l,int r,int L,int R,int k) {
if(L<=l&&r<=R) {
lazy[rt]+=k;
atre[rt]+=1LL*k*(r-l+1);
return;
}
int mid=l+((r-l)>>1);
if(lazy[rt])pushdown(rt,l,r);
if(L<=mid)update(rt<<1,l,mid,L,R,k);
if(R>mid)update(rt<<1|1,mid+1,r,L,R,k);
pushup(rt);
}
int qrange(int x,int y) {
int ans=0;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]])swap(x,y);
res=query(1,1,n,id[top[x]],id[x]);
ans+=res;
ans%=mod;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
res=query(1,1,n,id[x],id[y]);
ans+=res;
ans%=mod;
return ans;
}
void updrange(int x,int y,int k) {
k%=mod;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,1,n,id[top[x]],id[x],k);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(1,1,n,id[x],id[y],k);
}
int qson(int x) {
res=query(1,1,n,id[x],id[x]+siz[x]-1);
res%mod;
return res;
}
void updson(int x,int k) {
update(1,1,n,id[x],id[x]+siz[x]-1,k);
}
void dfs1(int x,int f,int deep) {
dep[x]=deep;
fa[x]=f;
siz[x]=1;
int maxson=-1;
for(int i=head[x]; i; i=nxt[i]) {
int y=to[i];
if(y==f) {
continue;
}
dfs1(y,x,deep+1);
siz[x]+=siz[y];
if(siz[y]>maxson)son[x]=y,maxson=siz[y];
}
}
void dfs2(int x,int topf){
id[x]=++cnt;
wt[cnt]=w[x];
top[x]=topf;
if(!son[x])return;
dfs2(son[x],topf);
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x]||y==son[x])continue;
dfs2(y,y);
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&r,&mod);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
}
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
addtre(a,b);
addtre(b,a);
}
dfs1(r,0,1);
dfs2(r,r);
build(1,1,n);
while(m--){
int k,x,y,z;
scanf("%d",&k);
if(k==1){
scanf("%d%d%d",&x,&y,&z);
updrange(x,y,z);
}
else if(k==2){
scanf("%d%d",&x,&y);
printf("%d\n",qrange(x,y));
}
else if(k==3){
scanf("%d%d",&x,&y);
updson(x,y);
}
else{
scanf("%d",&x);
printf("%d\n",qson(x));
}
}
}