树链剖分 学习笔记

时间:2019-10-22
本文章向大家介绍树链剖分 学习笔记,主要包括树链剖分 学习笔记使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

树链剖分 MOD

luogu 3384 模板

内容主要就是如何在树上用线段树

问题有两个:

  1. 区间修改某一节点的子树并统计和
  2. 区间修改两节点间的最短路上的节点并统计和

这里就要引入DFS序的概念

如上图,可以发现一棵树的前序遍历会使得一个节点的
子树是一个连续的序列,如果我们建一个数组来记录每个节点所能到达的最深的子节点,我们就能用线段树来维护
它了。

这样我们就解决了问题1,但问题2的操作节点编号是不连续的,我们要怎样处理呢?

如以上述方法标号后的图上两点 (5,8)

我们发现其实图上还是有连续序列的,那么就可以对这些区间分段操作。
如图,可以发现节点 5 在 1->5 这条链上;
而 8 可以单独看作一条链,从 8 到 5 的区间就可以用几条链表示出来,那么图中任意两点皆可如此。

具体操作如下:

1. 找到其中链头较深的节点,将其指针移至链头的父节点,并把链头至原节点之间的连续区间在线段树上修改。

2.重复步骤 1 ,直到两节点在一条链上。

3.对两节点之间的连续区间修改

但是问题来了,如何让链尽量少(也就是链尽量长,因为明显这样线段操作会更多)。

我们发现,子树“大”的节点链会更长,那么只需统计一个节点的 子树最大的 子节点,然后先搜这个节点为其编号。

为此我们要先dfs一遍找子树最大的子节点(顺便把深度,父节点等等预处理掉)

贴代码:
void dfs1(int o,int fx){ //当前节点编号和父节点编号 dep[o]=dep[fx]+1; //深度 fa[o]=fx; //父节点 siz[o]=1; // siz[]记录子树大小 hv[o]=o; int ma=0; for(int i=head[o];i!=0;i=e[i].next){ int x=e[i].to; if(x==fx) continue; dfs1(x,o); siz[o]+=siz[x]; if(ma <= siz[x]){ //找出// 子树最大的子节点 ma=siz[x]; hv[o]=x; } } }

然后就是编号,这里注意所有数组下标都是原编号而不是我们新给的编号

void dfs2(int o,int topp){
    top[o]=topp;  // top[] 存链顶
    dfn[o]=++cnt; // 编号
    w[cnt]=val[o]; //将原节点的权值转到新编号上
    if(hv[o]!=o)  // 在叶节点上不能再下搜了 不然会死循环
    dfs2(hv[o],topp); //先搜最重节点
    for(int i=head[o];i!=0;i=e[i].next){
        int x=e[i].to;
        if(x==fa[o] || x==hv[o]) continue;
        dfs2(x,x); //新开一个链
    }
}

那么为什么这样会快呢?
不难发现,每一次跳跃最少会减少 1 的深度,
就算每次都跳,也只有 \(\lg{n}\) 次( \(n\)是节点数)。而每个链的线段树操作是\(\lg{n}\) 的。
所以最终的时间复杂度是 $ O(\lg^2{n})$.

剩下就是线段树了。注意树是以新编号建的而数组都是用原编号当的下标。

最后是代码:

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 100050



struct node{
    int next,to;
};
node e[maxn<<1];
int n,m,R,p,cnt;
int dfn[maxn],fa[maxn],siz[maxn],hv[maxn],dep[maxn],val[maxn],head[maxn],top[maxn],w[maxn];
int tree[maxn<<2],lazy[maxn<<2];

void add(int from,int to){
    e[++cnt].next=head[from];
    e[cnt].to=to;
    head[from]=cnt;
}
 
void dfs1(int o,int fx){
    dep[o]=dep[fx]+1;
    fa[o]=fx;
    siz[o]=1;
    hv[o]=o;
    int ma=0;
    for(int i=head[o];i!=0;i=e[i].next){
        int x=e[i].to;
        if(x==fx) continue;
        dfs1(x,o);
        siz[o]+=siz[x];
        if(ma <= siz[x]){
            ma=siz[x];
            hv[o]=x;
        }
    }
}
 
void dfs2(int o,int topp){
    top[o]=topp;
    dfn[o]=++cnt;
    w[cnt]=val[o];
    if(hv[o]!=o) 
    dfs2(hv[o],topp);
    for(int i=head[o];i!=0;i=e[i].next){
        int x=e[i].to;
        if(x==fa[o] || x==hv[o]) continue;
        dfs2(x,x);
    }
}
 
void pushup(int o){
    tree[o]=tree[o<<1]+tree[o<<1|1];
    tree[o]%=p;
}
 
void pushdown(int o,int l,int r){
    if(lazy[o]==0) return;
    int mid=(r+l)>>1;
    lazy[o<<1]+=lazy[o];
    lazy[o<<1|1]+=lazy[o];
    tree[o<<1]+=lazy[o]*(mid-l+1);
    tree[o<<1|1]+=lazy[o]*(r-mid);
    lazy[o<<1]%=p;
    lazy[o<<1|1]%=p;
    tree[o<<1]%=p;
    tree[o<<1|1]%=p;
    lazy[o]=0;
} 

void plus(int o,int l,int r,int ql,int qr,int v){
    if(ql<=l && r<=qr){
        tree[o]+=v*(r-l+1);
        tree[o]%=p;
        lazy[o]+=v;
        lazy[o]%=p;
        return; 
    }
    pushdown(o,l,r);
    int mid=(r+l)>>1;
    if(ql<=mid) plus(o<<1,l,mid,ql,min(qr,mid),v);
    if(qr>mid) plus(o<<1|1,mid+1,r,max(mid+1,ql),qr,v);
    pushup(o);
}
 
 
void build(int o,int l,int r){
    if(r<=l){
        tree[o]=w[l];
        return;
    }
    int mid=(r+l)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}
 
int query(int o,int l,int r,int ql,int qr){
    if(ql<=l && r<=qr) return tree[o];
    pushdown(o,l,r);
    int mid=(r+l)>>1,ret=0;
    if(ql<=mid) ret+=query(o<<1,l,mid,ql,min(qr,mid)),ret%=p;
    if(qr>mid) ret+=query(o<<1|1,mid+1,r,max(ql,mid+1),qr),ret%=p;
    return ret;
}

void plus1(int x,int y,int v){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        plus(1,1,n,dfn[top[x]],dfn[x],v);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    plus(1,1,n,dfn[x],dfn[y],v);
}

void plus2(int x,int v){
    plus(1,1,n,dfn[x],dfn[x]+siz[x]-1,v);
}
 
int query1(int x,int y){
    int ret=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ret+=query(1,1,n,dfn[top[x]],dfn[x]),ret%=p;
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ret+=query(1,1,n,dfn[x],dfn[y]);
    return ret%=p;
}

int query2(int x){
    return query(1,1,n,dfn[x],dfn[x]+siz[x]-1)%p;
}
  
void work(){
    dfs1(R,0);
    dfs2(R,R);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int opt,x,y,z;
        scanf("%d %d",&opt,&x);
        if(opt==1){
            scanf("%d %d",&y,&z);
            plus1(x,y,z);
        }
        if(opt==2){
            scanf("%d",&y);
            printf("%d\n",query1(x,y));
        }
        if(opt==3){
            scanf("%d",&y);
            plus2(x,y);
        }
        if(opt==4){
            printf("%d\n",query2(x));
        }
    }
}
 
void input(){
    scanf("%d %d %d %d",&n,&m,&R,&p);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
    }
    for(int i=1;i<n;i++){
        int tp1,tp2;
        scanf("%d %d",&tp1,&tp2);
        add(tp1,tp2);
        add(tp2,tp1);
    }
    cnt=0;
}
 
int main(){
    input();
    work();
    return 0;
}

· by thorn

原文地址:https://www.cnblogs.com/thornblog/p/11718381.html