树链剖分

Posted downrainsun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分相关的知识,希望对你有一定的参考价值。

https://www.cnblogs.com/chinhhh/p/7965433.html#dfs1

 

 

 树链剖分跳链logn的复杂度。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N =200000+10;
vector<int> g[N];
int w[N],wt[N];
//w[]初始的、wt[]新的点权数组
int a[N<<2],laz[N<<2];
//线段树数组、lazy操作
int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N];
//son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
int res;
//查询答案
int n,m,r,mod;
//-------------------------------------- 以下为线段树
inline void pushdown(int rt,int len)
    if(laz[rt]) 
        laz[rt<<1]+=laz[rt];
        laz[rt<<1|1]+=laz[rt];
        a[rt<<1]+=laz[rt]*(len-(len>>1));
        a[rt<<1|1]+=laz[rt]*(len>>1);
        a[rt<<1]%=mod;
        a[rt<<1|1]%=mod;
        laz[rt]=0;
    


void pushup(int rt) 
    a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;


void build(int rt,int l,int r)
    if(l==r)
        a[rt]=wt[l];
        if(a[rt]>mod)a[rt]%=mod;
        return;
    
    int m = (l + r) >> 1;
    build(rt << 1, l, m);
    build(rt << 1 | 1, m + 1, r);
    pushup(rt);


void query(int rt,int l,int r,int le,int re)
    if(le<=l&&r<=re)
        res+=a[rt];
        res%=mod;
        return;
    
    pushdown(rt,r - l + 1);
    int m = (l + r) >> 1;
    if(re <= m) query(rt << 1, l, m, le, re);
    else if(le > m) query(rt << 1 | 1, m + 1, r, le, re);
    else 
        query(rt << 1, l, m, le, m);
        query(rt << 1 | 1, m + 1, r, m + 1, re);
    


void update(int rt,int l,int r,int le,int re,int k)
    if(le<=l&&re >= r)
        laz[rt]+=k;
        a[rt]+=k*(r - l + 1);
        if(a[rt] > mod) a[rt] %= mod;
        return;
    
    pushdown(rt,r - l + 1);
    int m = (l + r) >> 1;
    if(re <= m) update(rt << 1, l, m, le, re, k);
    else if(le > m) update(rt << 1 | 1, m + 1, r, le, re, k);
    else 
        update(rt << 1, l, m, le, m, k);
        update(rt << 1 | 1, m + 1, r, m + 1, re, k);
    
    pushup(rt);

//---------------------------------以上为线段树
int qRange(int x,int y)
    int ans=0;
    while(top[x]!=top[y])//当两个点不在同一条链上
        if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
        res=0;
        query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
        ans+=res;
        ans%=mod;//按题意取模
        x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
    
    //直到两个点处于一条链上
    if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点
    res=0;
    query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可
    ans+=res;
    return ans%mod;


void updRange(int x,int y,int k)//同上
    k%=mod;
    while(top[x]!=top[y])
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        update(1,1,n,id[top[x]],id[x],k);
        x=fa[top[x]];
    
    if(dep[x]>dep[y])swap(x,y);
    update(1,1,n,id[x],id[y],k);


int qSon(int x)
    res=0;
    query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1
    return res;


void updSon(int x,int k)//同上
    k %= mod;
    update(1,1,n,id[x],id[x]+siz[x]-1,k);


void dfs1(int x,int f,int deep)//x当前节点,f父亲,deep深度
    dep[x]=deep;//标记每个点的深度
    fa[x]=f;//标记每个点的父亲
    siz[x]=1;//标记每个非叶子节点的子树大小
    int maxson=-1;//记录重儿子的儿子数
    int len = g[x].size();
    for(int i = 0; i < len; i++)
        int y=g[x][i];
        if(y==f)continue;//若为父亲则continue
        dfs1(y,x,deep+1);//dfs其儿子
        siz[x]+=siz[y];//把它的儿子数加到它身上
        if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号
    


void dfs2(int x,int topf)//x当前节点,topf当前链的最顶端的节点
    id[x]=++cnt;//标记每个点的新编号
    wt[cnt]=w[x];//把每个点的初始值赋到新编号上来
    top[x]=topf;//这个点所在链的顶端
    if(!son[x])return;//如果没有儿子则返回
    dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
    int len = g[x].size();
    for(int i = 0; i < len; i++)
        int y=g[x][i];
        if(y==fa[x]||y==son[x])continue;
        dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链
    


int main()
    scanf("%d%d%d%d", &n, &m, &r, &mod);
    for(int i=1;i<=n;i++) scanf("%d", &w[i]);
    int u, v;
    for(int i=1;i<n;i++)
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    
    cnt = 0;
    dfs1(r,0,1);
    dfs2(r,r);
    build(1,1,n);
    while(m--)
        int k,x,y,z;
        scanf("%d", &k);
        if(k==1)
            scanf("%d%d%d", &x, &y, &z);
            updRange(x,y,z);
        
        else if(k==2)
            scanf("%d%d", &x, &y);
            printf("%d\\n",qRange(x,y));
        
        else if(k==3)
            scanf("%d%d", &x, &y);
            updSon(x,y);
        
        else
            scanf("%d", &x);
            printf("%d\\n",qSon(x));
        
    

 

以上是关于树链剖分的主要内容,如果未能解决你的问题,请参考以下文章

树链剖分模板

树链剖分

树链剖分 入门

树链剖分

树链剖分(轻/重链剖分学习笔记)

树链剖分详解