树链剖分
写在前面
某位菜鸡花了半天的时间打完了树剖,又用了半天的时间di了无数个bug后,终于获得了MLE,RE并存的喜人成绩,最终在 \(ljc\) 大佬的指点下才发现原来是函数void写成int了并且没写返回值 T_T
正题
懒得自己写一篇博客了,就把我当时学习树剖时的一篇写的非常好的博客拿出来欣赏吧
博客原文
几条重要的规则
- 一个字树内的dfs序连续,线段树维护3,4操作
- 节点数多的叫做重儿子,重儿子到父亲节点的边叫做重边
- 每一条链dfs序连续(先遍历重儿子)
- 跳到同一条重链上深度小的即为最近公共祖先
dfs1():
统计每个节点的深度 \(deep[ ]\) ,每个节点父亲 \(fa[ ]\) ,每个节点子树大小 \(size[ ]\) ,重儿子编号 \(mson[]\)
dfs2():
每个旧节点新编号 \(id[]\) ,每个新节点的旧编号 \(di[]\) ,新编号的值 \(newa[]\) ,每个节点所在重链顶端 \(top[]\)
警示后人
一定要处理好新编号和旧编号的关系,最好采用( \(id\) 和 \(di\) )双重索引,注意你的变量是哪一次dfs()维护的,注意新旧节点的转换
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int>b[N];
int deep[N],size[N],fa[N],mson[N],a[N],newa[N],top[N],id[N],t[4*N],add[4*N],di[N];
int cnt,n,m,st,v,p,u,op,z;
void dfs1(int x,int y){//节点是x,父亲是y
deep[x]=deep[y]+1;
fa[x]=y;
int maxson=0;
for(auto i:b[x]){
if(i==y) continue;//双向建边的危害
dfs1(i,x);
if(size[i]>maxson){
mson[x]=i;
maxson=size[i];
}
}
size[y]+=size[x];
}
void dfs2(int x,int topy){
if(x==0) return;//叶子节点访问记得特判
id[x]=++cnt;
di[cnt]=x;
newa[cnt]=a[x];
top[cnt]=id[topy];
dfs2(mson[x],topy);
for(int i:b[x]){
if(i==fa[x]) continue;
if(i==mson[x]) continue;
dfs2(i,i);
}
}
void build(int k,int l,int r){
if(l==r){
t[k]=newa[l];
return;
}
int mid=(l+r)>>1;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
t[k]=t[2*k]+t[2*k+1];
t[k]%=p;
}
void Add(int k,int l,int r,int vv){//int 大寄
t[k]+=vv*(r-l+1);
t[k]%=p;
add[k]+=vv;
add[k]%=p;
}
void pushdown(int k,int l,int r){
int mid=(l+r)>>1;
Add(2*k,l,mid,add[k]);
Add(2*k+1,mid+1,r,add[k]);
add[k]=0;
}
int longqure(int k,int l,int r,int x,int y){
if(x<=l&&r<=y){
return t[k];
}
pushdown(k,l,r);
int mid=(l+r)>>1;
int res=0;
if(x<=mid) res+=longqure(2*k,l,mid,x,y);
if(mid<y) res+=longqure(2*k+1,mid+1,r,x,y);
res%=p;
return res;
}
void longchange(int k,int l,int r,int x,int y,int vv){
if(x<=l&&r<=y){
t[k]+=vv*(r-l+1);
t[k]%=p;
add[k]+=vv;
add[k]%=p;
return;
}
pushdown(k,l,r);
int mid=(l+r)>>1;
if(x<=mid) longchange(2*k,l,mid,x,y,vv);
if(mid<y) longchange(2*k+1,mid+1,r,x,y,vv);
t[k]=t[2*k]+t[2*k+1];
t[k]%=p;
return;
}
int lca(int x,int y,int vv,bool cz){
int res=0;
while(top[x]!=top[y]){
// printf("x=%d y=%d\n",x,y);
if(deep[di[top[x]]]<deep[di[top[y]]]) swap(x,y);
if(cz) longchange(1,1,n,top[x],x,vv);
else res+=longqure(1,1,n,top[x],x);
// printf("lca= %d %d %d %d %d %d\n",di[top[x]],di[x],di[y],top[x],x,y);
x=id[fa[di[top[x]]]];
// printf("fa=%d\n",x);
}
// printf("x=%d y=%d\n",x,y);
if(deep[di[x]]>deep[di[y]]) swap(x,y);
if(cz) longchange(1,1,n,x,y,vv);
else res+=longqure(1,1,n,x,y);
// printf("lca= %d %d %d %d\n",di[x],di[y],x,y);
return res;
}
int main(){
scanf("%d%d%d%d",&n,&m,&st,&p);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
size[i]=1;
}
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
b[u].push_back(v);//双向建边
b[v].push_back(u);
}
dfs1(st,0);
dfs2(st,st);
build(1,1,n);
// for(int i=1;i<=n;i++){
// printf("i=%d id=%d a=%d t=%d s=%d\n",i,id[i],newa[i],top[i],id[mson[i]]);
// }
for(int i=1;i<=m;i++){
scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&u,&v,&z);
lca(id[u],id[v],z,1);
}
if(op==2){
scanf("%d%d",&u,&v);
printf("%d\n",lca(id[u],id[v],0,0)%p);
}
if(op==3){
scanf("%d%d",&u,&z);
// printf("u=%d %d\n",id[u],size[u]);
longchange(1,1,n,id[u],id[u]+size[u]-1,z);
}
if(op==4){
scanf("%d",&u);
printf("%d\n",longqure(1,1,n,id[u],id[u]+size[u]-1)%p);
}
// for(int i=1;i<=n;i++){
// printf(" %d %d\n",i,longqure(1,1,n,id[i],id[i]));
// }
}
}