树链剖分回忆笔记

树链剖分回忆笔记

摘抄定义:

  1. 重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点;
  2. 轻儿子:父亲节点中除了重儿子以外的儿子;
  3. 重边:父亲结点和重儿子连成的边;
  4. 轻边:父亲节点和轻儿子连成的边;
  5. 重链:由多条重边连接而成的路径;
  6. 轻链:由多条轻边连接而成的路径;

然后就没有然后了

所有有关于“轻”字的东西都没啥用

对着图理解会好一点

标红点点的是一条重链的起点

我们发现,1、2、3、4、8、10、11号边都是重边

更重要的是,所有节点都在一个重链上(一个结点的也算)

dfs序在树链剖分中的定义也有一点不一样

由于为了维护方便,我们把一条链上节点的DFS序连续

扫描二维码关注公众号,回复: 7420025 查看本文章

树链剖分大名鼎鼎的两个dfs就在预处理dfs序、深度、轻重儿子、轻重链、子树大小

然后树链剖分的主要思想就是对重链进行各种高级数据结构维护,例如线段树、平衡树......

好吧,就这样

板子题代码:

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
long long n,m,r,p;//树的结点个数、操作个数、根节点序号和取模数
long long val[N];//每个节点的初始权值 
long long cnt=0,hed[N],tal[N*2],nxt[N*2];
long long f[N];//父亲节点
long long d[N];//节点深度
long long size[N];//子树节点个数
long long s[N]={0};//重儿子编号 
long long id[N];//DFS序
long long Rk[N];//该dfs序在原树中对应的编号 
long long T[N];//链顶 
long long tot=0;//时间戳  
struct ST{
    long long value;
    long long LZT;
}t[N<<2];//线段树 
void addege(long long a,long long b){
    cnt++;
    tal[cnt]=b;
    nxt[cnt]=hed[a];
    hed[a]=cnt; 
}
void dfs1(long long u,long long fa,long long dis){//第一遍dfs 
    d[u]=dis;//节点初始化 
    f[u]=fa;
    size[u]=1;
    for(long long i=hed[u];i;i=nxt[i]){
        long long v=tal[i];
        if(v==fa) continue;
        dfs1(v,u,dis+1);//遍历儿子 
        size[u]+=size[v];//更新子树节点个数 
        if(s[u]==0) s[u]=v;
        else if(size[s[u]]<size[v]) s[u]=v;//选择重儿子 
    }
}
void dfs2(long long u,long long gf){//当前节点、链顶
     T[u]=gf;
    id[u]=++tot;
    Rk[tot]=u;
    if(!s[u]) return; 
    dfs2(s[u],gf);
    for(long long i=hed[u];i;i=nxt[i]){
        long long v=tal[i];
        if(v==f[u]) continue;
        if(v==s[u]) continue;
        dfs2(v,v);  
    } 
}
long long len(long long l,long long r){return r-l+1;}
void push_up(long long num){
    t[num].value=(t[num<<1].value+t[num<<1|1].value)%p;
}
void push_down(long long l,long long r,long long num){
    if(t[num].LZT){
        long long mid=(l+r)>>1;
        t[num<<1].LZT+=t[num].LZT;
        t[num<<1|1].LZT+=t[num].LZT;
        t[num<<1].value+=len(l,mid)*t[num].LZT;
        t[num<<1].value%=p;
        t[num<<1|1].value+=len(mid+1,r)*t[num].LZT;
        t[num<<1|1].value%=p;
        t[num].LZT=0;
    }
} 
void build(long long l,long long r,long long num){
    if(l==r){
        t[num].LZT=0;
        t[num].value=val[Rk[l]]%p;
        return;
    }
    long long mid=(l+r)>>1;
    build(l,mid,num<<1),build(mid+1,r,num<<1|1);
    push_up(num);
}
void upt(long long l,long long r,long long num,long long L,long long R,long long X){
    if(l>=L&&r<=R){
        t[num].LZT+=X;
        t[num].LZT%=p; 
        t[num].value+=len(l,r)*X;
        t[num].value%=p; 
        return;
    }
    if(l>R||r<L) return;
    long long mid=(l+r)>>1;
    push_down(l,r,num);
    upt(l,mid,num<<1,L,R,X);
    upt(mid+1,r,num<<1|1,L,R,X);
    push_up(num);
}
long long qus(long long l,long long r,long long num,long long L,long long R){
    if(l>=L&&r<=R){
        return t[num].value%p;
    }
    if(l>R||r<L) return 0;
    long long mid=(l+r)>>1;
    push_down(l,r,num);
    return (qus(l,mid,num<<1,L,R)+qus(mid+1,r,num<<1|1,L,R))%p;
}
long long ask(long long x,long long y){
    long long sum=0;
    while(T[x]!=T[y]){
        if(d[T[x]]<d[T[y]]) swap(x,y);
        sum+=qus(1,n,1,id[T[x]],id[x]);
        sum%=p;
        x=f[T[x]];
    } 
    if(d[x]>d[y]) swap(x,y);
    return (sum+qus(1,n,1,id[x],id[y]))%p;
} 
void pus(long long x,long long y,long long z){
    while(T[x]!=T[y]){
        if(d[T[x]]<d[T[y]]) swap(x,y);
        upt(1,n,1,id[T[x]],id[x],z);
        x=f[T[x]];
    } 
    if(d[x]>d[y]) swap(x,y);
    upt(1,n,1,id[x],id[y],z);
} 
int main(){
    scanf("%lld%lld%lld%lld",&n,&m,&r,&p);
    for(long long i=1;i<=n;i++){
        scanf("%lld",&val[i]); 
    }
    for(long long i=1;i<n;i++){
        long long a,b;
        scanf("%lld%lld",&a,&b);
        addege(a,b);
        addege(b,a);
    }
    dfs1(r,-1,1);//从root出发,求出每个节点的深度、子树节点个数、父亲编号以及重儿子
    //// 节点,父亲节点,深度
    dfs2(r,r);//第二遍dfs求出其余信息 
    //--------------------------------------------------------------------------
    //接下来进入线段树部分!
    build(1,n,1);//建树  
    while(m--){
        long long opt;
        scanf("%lld",&opt);
        if(opt==1){
            long long x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            z%=p;
            pus(x,y,z);
        }
        else if(opt==2){
            long long x,y;
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",ask(x,y)%p);
        }
        else if(opt==3){
            long long x,y;
            scanf("%lld%lld",&x,&y);
            y%=p;
            upt(1,n,1,id[x],id[x]+size[x]-1,y);
        }
        else{
            long long x;
            scanf("%lld",&x);
            printf("%lld\n",qus(1,n,1,id[x],id[x]+size[x]-1)%p);
        }
    }
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/QYJ060604/p/11620624.html
今日推荐