题解 P3384 【【模板】树链剖分】
Dvelpro
2018-08-11 02:45:05
解释全部在代码之中 卿学姐的视频很不错
[一点就懂的 树剖](https://www.bilibili.com/video/av4482146?from=search&seid=16528902309319633365)
以下代码部分
```c
#include<bits/stdc++.h>
using namespace std;
#define maxn 500000+10
#define LL long long
LL no[maxn]; /// 所属链的编号
LL top[maxn]; /// top节点
LL dee[maxn]; /// 深度(然而我并没有用到)
LL fa[maxn]; /// 父亲节点
LL zs[maxn]; /// 重儿子
LL sz[maxn]; /// 子节点个数
LL dfn[maxn]; /// dfs的编号
LL tre[maxn]; /// 建树
LL a[maxn],b[maxn]; /// 储存节点的value
bool vis[maxn]; /// 第一遍 dfs 的标记数组
long long lz[maxn];/// 懒惰数组
vector<long long>q[maxn]; /// 记录节点信息
long long n,m,r,P;
long long tot=0,z=1; /// dfs的编号 和链的编号
void dfs(long long u,long long y){ ///第一遍dfs记录 节点的深度,重儿子,节点个数
sz[u]=1;
dee[u]=y;
long long mx=-1;
for(long long j=0;j<q[u].size();j++){
long long v=q[u][j];
if(!vis[v]){
vis[v]=1;
fa[v]=u;
dfs(v,y+1);
sz[u]+=sz[v];
if(sz[v]>mx){
mx=sz[v];
zs[u]=v;
}
}
}
}
void dfs2(long long u,long long y){ ///第二遍dfs 记录 每条链的编号以及每个节点的编号
dfn[u]=++tot;
b[tot]=a[u];
top[u]=y;
no[u]=z;
if(zs[u]==0) return ;
if(zs[u]){
dfs2(zs[u],y);
}
for(long long j=0;j<q[u].size();j++){
long long v=q[u][j];
if(v!=zs[u]&&v!=fa[u]){
z++;
dfs2(v,v);
}
}
}
void lazy(long long in,long long rt){
if(lz[in]){
lz[in*2]+=lz[in];
lz[in*2+1]+=lz[in];
tre[in*2]+=lz[in]*(rt-rt/2);
tre[in*2+1]+=lz[in]*(rt/2);
lz[in]=0;
}
}
void build(long long l,long long r,long long in){
if(l==r){
tre[in]=b[l];
return ;
}
long long mid=(l+r)/2;
build(l,mid,in*2);
build(mid+1,r,in*2+1);
tre[in]=tre[in*2]+tre[in*2+1];
}
long long query(long long l,long long r,long long x,long long y,long long in){
if(l==x&&r==y){
return tre[in]%P;
}
lazy(in,y-x+1);
long long mid=(x+y)/2;
if(r<=mid){
return query(l,r,x,mid,in*2)%P;
}else if(l>mid){
return query(l,r,mid+1,y,in*2+1)%P;
}
return (query(l,mid,x,mid,in*2)%P+query(mid+1,r,mid+1,y,in*2+1)%P)%P;
}
void updata(long long l,long long r,long long x,long long y,long long in,long long c){
if(l==x&&r==y){
tre[in]+=c*(y-x+1);
lz[in]+=c;
return ;
}
lazy(in,y-x+1);
long long mid=(x+y)/2;
if(r<=mid){
updata(l,r,x,mid,in*2,c);
}else if(l>mid){
updata(l,r,mid+1,y,in*2+1,c);
}
else{
updata(l,mid,x,mid,in*2,c);
updata(mid+1,r,mid+1,y,in*2+1,c);
}
tre[in]=tre[in*2]+tre[in*2+1];
}
/// 以上正常的线段树操作
void xiugai1(long long x,long long y,long long z){
long long xx=no[x],yy=no[y]; /// 属于那条链
if(xx==yy){ /// 判断链的编号大小然后做出修改
long long xxx=dfn[x],yyy=dfn[y]; /// 相同 属于同一条链 同一条链的编号是连续的
if(xxx>yyy) swap(xxx,yyy);
updata(xxx,yyy,1,n,1,z);
return ;
}
if(xx>yy){ /// x>y 修改x所在链的区间
updata(dfn[top[x]],dfn[x],1,n,1,z);
xiugai1(fa[top[x]],y,z); /// 递归寻找其top节点的父亲节点的链和y比较
}
if(xx<yy){
updata(dfn[top[y]],dfn[y],1,n,1,z);
xiugai1(x,fa[top[y]],z);
}
}
long long chaxun1(long long x,long long y){ /// 和修改操作基本一致
long long xx=no[x],yy=no[y];
if(xx==yy){
long long xxx=dfn[x],yyy=dfn[y];
if(xxx>yyy) swap(xxx,yyy);
return query(xxx,yyy,1,n,1)%P;
}
long long ans=0;
if(xx>yy){
ans+=query(dfn[top[x]],dfn[x],1,n,1)%P;
ans%=P;
ans+=chaxun1(fa[top[x]],y)%P;
ans%=P;
}
if(xx<yy){
ans+=query(dfn[top[y]],dfn[y],1,n,1)%P;
ans%=P;
ans+=chaxun1(x,fa[top[y]])%P;
ans%=P;
}
return ans%P;
}
long long chaxun(long long x){ /// 某一节点的子树的编号一定连续 只要找到 这个节点的sz个数和 dfn 编号即可
return query(dfn[x],dfn[x]+sz[x]-1,1,n,1)%P;
}
void xiugai(long long x,long long y){ /// 同上
updata(dfn[x],dfn[x]+sz[x]-1,1,n,1,y);
}
/// 以下就是常规的操作
int main(){
cin>>n>>m>>r>>P;
memset(lz,0,sizeof(lz));
for(long long j=1;j<=n;j++){
cin>>a[j];
}
memset(vis,0,sizeof(vis));
for(long long j=1;j<n;j++){
long long x,y;
cin>>x>>y;
q[x].push_back(y);
q[y].push_back(x);
}
vis[r]=1;
dfs(r,1LL*1);
memset(vis,0,sizeof(vis));
dfs2(r,r);
build(1,n,1);
for(long long j=0;j<m;j++){
long long nu,x,y,z;
cin>>nu;
if(nu==1){
cin>>x>>y>>z;
xiugai1(x,y,z);
}
if(nu==2){
cin>>x>>y;
cout<<chaxun1(x,y)%P<<endl;;
}
if(nu==3){
cin>>x>>y;
xiugai(x,y);
}
if(nu==4){
cin>>x;
cout<<chaxun(x)%P<<endl;
}
}
return 0;
}
```