P3384 [Template] Light and heavy chain division (tree chain division to build line segment tree maintenance board)

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


   Content refer to Teacher Luo's blog     

   The heavy chain has important characteristics: the DFS sequence of the internal nodes of a heavy chain is continuous . This feature makes it possible to use a data structure (usually a line segment tree) to maintain the heavy chain, thereby efficiently solving some tree problems, such as the following problems:
   (1) Modify the weight of each point on the path from point x to point y .
   (2) Query the sum of node weights on the path from point x to point y.
   (3) Modify the weight of each point on the point x subtree.
   (4) Query the sum of the weights of all nodes on the subtree of point x.
   Among them, (1) is the problem of "difference on the tree". The difference on the tree can only solve the simple modification problem. For the problem of modifying the entire subtree like (3), the difference on the tree will not work.


1. The DFS sequence of the heavy chain The
   function dfs2() given earlier is that DFS is the heaviest son first, and then DFS is the heaviest son. If you use the number id[x] to record the DFS sequence of node x in the first sentence of dfs2():
      id[x] = ++num;
   The result of renumbering each node, for example, the following figure:

 It is easy to observe:
   (1) The numbers of the internal nodes of each heavy chain are ordered . The DFS order of the heavy chain {a, b, e, j, q} is {1, 2, 3, 4, 5}; the DFS order of the heavy chain {d, p} is {7, 8}; the heavy chain {c The DFS order of, f} is {10, 11}.
   (2) The DFS order of all nodes on each subtree is also continuous . For example, for subtrees {e, i, j, q} rooted at e, their DFS order is {3, 4, 5, 6}.
   Here is the key content: Use line segment trees to handle heavy chains. Since the nodes inside each heavy chain are ordered, they can be arranged in a line segment tree in DFS order. Regarding each heavy chain as a continuous interval , the modification and query of a heavy chain are handled by a line segment tree; if the path from x to y crosses multiple heavy chains, simply skip it.
   In a nutshell: "The line segment tree is used inside the heavy chain, and the heavy chain is skipped ."

 

The above figure rearranges the nodes of the tree chain on a line segment tree. The nodes of the same heavy chain are continuous on the line segment tree.

2. Modify the shortest path of node weights
  x and y on the shortest path from node x to y through LCA(x, y). This is actually a process of finding LCA(x, y). Use the heavy chain to modify the weights of the nodes on the path:
  (1) Make the depth of the chain head of x deeper, that is, top[x] ≥ top[y]. Starting from x and going up, first go up along the heavy chain where x is located, and modify the nodes of this section;
  (2) After reaching the chain head of x, skip a light side and reach the previous heavy chain;
  ( 3) Continue to execute (1) and (2) until x and y are on the same heavy chain, and then modify the node weight between the two points at this time. End.
  For example, modify the sum of the weights of all nodes on the path from p to q:
  (1) Go from p to its chain head top[p] = d, modify the weights of p and d;
  (2) jump to b;
  (3) b and q are on the same heavy chain, modify the weight from b to q; end.
  Use the line segment tree to handle the above process, and still take the modification of the sum of the nodes on the path from p to q as an example:
  (1) Jump from p to the chain head d, p and d belong to the same heavy chain, use the line segment tree to modify the corresponding [7, 8] Interval;
  (2) Cross the light side (b, d) from d to the heavy chain where b is located;
  (3) Check b to q, they belong to the same heavy chain, use the line segment tree to modify the corresponding interval [2, 5], the end.

3. Query the sum of the weights of all nodes on the path from x to y.
  The process of query and modification is almost the same. Take querying the sum of nodes on the path from p to q as an example:
  (1) Jump from p to The chain heads d, p and d belong to the same heavy chain, use the line segment tree to query the corresponding interval [7, 8];
  (2) Cross the light side (b, d) from d to the heavy chain where b is located;
  (3 ) Check b to q, they belong to the same heavy chain, use the line segment tree to query the corresponding interval [2, 5], and end.

4. Modify the weight of each point on the subtree of node x, and query the sum of the weight of the node on the subtree of node x.
  The DFS order of all nodes on each subtree is continuous, that is, every A subtree corresponds to a continuous interval. Then the modification and query of the subtree are exactly the same as the modification and query operations of the segment tree to the interval.


Reference blog: https://blog.csdn.net/weixin_43914593/article/details/109709506


Idea: After the tree chain is split, assign the original value to the new number, and use the new number to build the line segment tree.

#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;
}

 

Guess you like

Origin blog.csdn.net/zstuyyyyccccbbbb/article/details/109860444