码迷,mamicode.com
首页 > 其他好文 > 详细

树链剖分

时间:2019-08-18 22:22:56      阅读:99      评论:0      收藏:0      [点我收藏+]

标签:clock   long   一段   range   nbsp   递归   continue   const   upd   

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

 

树链剖分

标签:clock   long   一段   range   nbsp   递归   continue   const   upd   

原文地址:https://www.cnblogs.com/downrainsun/p/11373882.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!