P3384 【模板】轻重链剖分(树链剖分建线段树维护板子)

https://www.luogu.com.cn/problem/P3384


   内容参考罗老师的博客     

   重链,有重要特征:一条重链内部结点的DFS序是连续的。这个特征使得可以用数据结构(一般是线段树)来维护重链,从而高效率地解决一些树上的问题,例如以下问题:
   (1)修改点x到点y的路径上各点的权值。
   (2)查询点x到点y的路径上结点权值之和。
   (3)修改点x子树上各点的权值。
   (4)查询点x子树上所有结点的权值之和。
   其中的(1)是“树上差分”问题.树上差分只能解决简单的修改问题,对(3)这样的修改整棵子树问题,树上差分就行不通了。


1、重链的DFS序
   前面给出的函数dfs2(),是先DFS重儿子,再DFS轻儿子。如果在dfs2()的第一句用编号id[x]记录结点x的DFS序:
      id[x] = ++num;
   对每个结点重新编号的结果,例如下图:

 容易观察到:
   (1)每一条重链内部结点的编号是有序的。重链{a, b, e, j, q}的DFS序是{1, 2, 3, 4, 5};重链{d, p}的DFS序是{7, 8};重链{c, f}的DFS序是{10, 11}。
   (2)每棵子树上的所有结点的DFS序也是连续的。例如以e为根的子树{e, i, j, q},它们的DFS序是{3, 4, 5, 6}。
   下面是关键内容:用线段树处理重链。由于每条重链内部的结点是有序的,可以按DFS序,把它们安排在一个线段树上。把每条重链看成一个连续的区间,对一条重链内部的修改和查询,用线段树来处理;若x到y的路径跨越了多个重链,简单地跳过即可。
   概况地说:“重链内部用线段树,重链之间跳过”。

 

上图把树链的结点重新安排在一个线段树上。同一个重链的结点,在线段树上是连续的。

2、修改从结点x到y的最短路径上结点权值
  x、y的最短路径经过LCA(x, y),这实际上是一个查找LCA(x, y)的过程。借助重链来修改路径上的结点的权值:
  (1)令x的链头的深度更深,即top[x] ≥ top[y]。从x开始往上走,先沿着x所在的重链往上走,修改这一段的结点;
  (2)到达x的链头后,跳过1个轻边,到达上一个重链;
  (3)继续执行(1)、(2),直到x和y位于同一条重链上,再修改此时两个点之间的结点权值。结束。
  例如修改从p到q的路径上所有结点权值之和:
  (1)从p走到它的链头top[p] = d,修改p和d的权值;
  (2)跳到b;
  (3)b和q在同一条重链上,修改从b到q的权值;结束。
  用线段树处理上述过程,仍以修改从p到q的路径上结点之和为例:
  (1)从p跳到链头d,p和d属于同一条重链,用线段树修改对应的[7, 8]区间;
  (2)从d穿过轻边(b, d),到达b所在的重链;
  (3)查b到q,它们属于同一个重链,用线段树修改对应区间[2, 5],结束。

3、查询从x到y的路径上所有结点权值之和
  查询与修改的过程几乎是一样的,以查询从p到q的路径上结点之和为例:
  (1)从p跳到链头d,p和d属于同一条重链,用线段树查询对应的[7, 8]区间;
  (2)从d穿过轻边(b, d),到达b所在的重链;
  (3)查b到q,它们属于同一个重链,用线段树查询对应区间[2, 5],结束。

4、修改结点x的子树上各点的权值、查询结点x的子树上结点权值之和
  每棵子树上的所有结点的DFS序是连续的,也就是说,每棵子树对应了一个连续的区间。那么修改和查询子树,和线段树对区间的修改和查询操作完全一样。


参考博客:https://blog.csdn.net/weixin_43914593/article/details/109709506


思路:树链剖分之后用原有值赋到新编号上去,用新编号来建立线段树.

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+2000;
typedef long long LL;
LL n,m,r,mod;
LL siz[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn];
LL id[maxn],tot=0;
LL a[maxn],a_new[maxn];
vector<LL>g[maxn];
///------------------线段树
struct Tree{
    LL l,r,sum,tag;
}tree[maxn*4];
void push_up(LL p){
    tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
    tree[p].sum%=mod;
}
void addtag(LL p,LL d){
    tree[p].tag+=d;
    tree[p].sum=(tree[p].sum%mod+d*(tree[p].r-tree[p].l+1)%mod)%mod;
}
void push_down(LL p){
    if(tree[p].tag!=0){
        addtag(p*2,tree[p].tag);
        addtag(p*2+1,tree[p].tag);
        tree[p].tag=0;
    }
}
void build(LL p,LL l,LL r){
    tree[p].l=l;tree[p].r=r;tree[p].sum=0;tree[p].tag=0;
    if(l==r) {tree[p].sum=a_new[l]%mod;tree[p].tag=0;return;}
    LL mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    push_up(p);
}
void modify(LL p,LL l,LL r,LL d){
    if(l<=tree[p].l&&r>=tree[p].r)
    {
        addtag(p,d);
        return;
    }
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) modify(p*2,l,r,d);
    if(r>mid) modify(p*2+1,l,r,d);
    push_up(p);
}
LL query(LL p,LL l,LL r){
    if(l<=tree[p].l&&r>=tree[p].r){
        return tree[p].sum%=mod;
    }
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    LL ans=0;
    if(l<=mid) ans+=query(p*2,l,r);
    if(r>mid) ans+=query(p*2+1,l,r);
    return ans;
}
///------------------树链剖分

void predfs(LL u,LL father)
{
    siz[u]=1;
    dep[u]=dep[father]+1;
    fa[u]=father;
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i];
        if(v==father) continue;
        predfs(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]){
            son[u]=v;
        }
    }
}
void dfs(LL u,LL topx)
{
    id[u]=++tot;
    a_new[tot]=a[u];///注意赋新值
    top[u]=topx;
    if(!son[u]) return;///叶子节点
    dfs(son[u],topx);///先处理重儿子
    for(LL i=0;i<g[u].size();i++){///处理轻儿子
        LL v=g[u][i];
        if(v==fa[u]||v==son[u]) continue;
        dfs(v,v);
    }
}
void changeLCArange(LL u,LL v,LL z){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]){
            swap(u,v);
        }
        modify(1,id[top[u]],id[u],z);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    modify(1,id[u],id[v],z);
}
LL queryLCAdis(LL u,LL v){
    LL ans=0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]){
            swap(u,v);
        }
        ans+=query(1,id[top[u]],id[u]);
        ans%=mod;
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    ans+=query(1,id[u],id[v]);
    return ans%=mod;
}
void changeShu(LL x,LL z){
    modify(1,id[x],id[x]+siz[x]-1,z);
}
LL queryShu(LL x){
    return query(1,id[x],id[x]+siz[x]-1)%mod;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  cin>>n>>m>>r>>mod;
  for(LL i=1;i<=n;i++){
    cin>>a[i];
  }
  for(LL i=1;i<n;i++){
    LL u,v;cin>>u>>v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  predfs(r,0);
  dfs(r,r);
  build(1,1,n);
  for(LL i=1;i<=m;i++){
    LL op;cin>>op;
    if(op==1){
        LL x,y,z;cin>>x>>y>>z;
        changeLCArange(x,y,z);
    }
    if(op==2){
        LL x,y;cin>>x>>y;
        cout<<queryLCAdis(x,y)<<endl;
    }
    if(op==3){
        LL x,z;cin>>x>>z;
        changeShu(x,z);
    }
    if(op==4){
        LL x;cin>>x;
        cout<<queryShu(x)<<endl;
    }
  }
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/109860444