BZOJ---4034: [HAOI2015]树上操作【树链剖分】

题面:

BZOJ----4034:[HAOI2015]树上操作

题意:

给定一颗树,支持三种操作:(1)单点修改(2)两点之间的最短路径上的所有点修改(3)询问两点之间的最短路径上的所有点的和

分析:

树链剖分真是个好东西,巧妙地将一棵树拍成线性的,这样就容易用数据结构维护区间问题了,与普通区间操作不同的是,它将树上两点之间的路径划分为logN个编号连续的区间,然后对每个区间进行普通操作即可,所以一次操作为O(logn*logn);树链剖分也可以O(logn)求出LCA

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 2e5+15;
int cnt,head[maxn],op,n,m,u,v,val,a[maxn];
struct edge{
    int to,nxt;
}e[maxn<<1];
inline void add(int u,int v){
    e[++cnt] = (edge){v,head[u]};
    head[u] = cnt;
}
int d[maxn],f[maxn],sz[maxn],son[maxn];
void dfs1(int x,int fa,int depth){
     f[x] = fa; d[x] = depth; sz[x] = 1;
     for(int i = head[x];i;i = e[i].nxt){
        int v = e[i].to;
        if(v == fa) continue;
        dfs1(v,x,depth+1); sz[x] += sz[v];
        if(sz[v] > sz[son[x]]) son[x] = v;
     } 
}
int rk[maxn],id[maxn],top[maxn],num;
void dfs2(int x,int Top){
    top[x] = Top; id[x] = ++num;
    rk[num] = x;
    if(!son[x]) return;
    dfs2(son[x],Top);
    for(int i = head[x];i;i = e[i].nxt){
        int v = e[i].to;
        if(v != son[x]&&v != f[x]) dfs2(v,v);
    }
}
/**************以上为树链剖分****************/
LL tr[maxn<<2],lazy[maxn<<2];
inline void pushdown(int x,int l,int r){
    int mid = (l+r)>>1;
    tr[x<<1] = (tr[x<<1] + 1ll*(mid-l+1)*lazy[x]);
    tr[x<<1|1] = (tr[x<<1|1]+1ll*(r-mid)*lazy[x]);
    lazy[x<<1] = (lazy[x<<1] + lazy[x]);
    lazy[x<<1|1] = (lazy[x<<1|1]+lazy[x]);
    lazy[x] = 0;
}
void build(int l,int r,int x){
    if(l == r){
        tr[x] = a[rk[l]];
        return ;
    }
    int mid = (l+r) >> 1;
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    tr[x] = (tr[x<<1]+tr[x<<1|1]);
}
void query(int l,int r,int L,int R,int x,LL &sum){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        sum = (sum+tr[x]);
        return ;
     }
    if(lazy[x]) pushdown(x,l,r);
    int mid = (l+r)>>1;
    query(l,mid,L,R,x<<1,sum);
    query(mid+1,r,L,R,x<<1|1,sum);
}
void updata(int l,int r,int L,int R,int x,LL val){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        tr[x] = (tr[x]+1ll*(r-l+1)*val);
        lazy[x] = (lazy[x]+val);
        return ;
    }
    if(lazy[x]) pushdown(x,l,r);
    int mid = (l+r)>>1;
    updata(l,mid,L,R,x<<1,val);
    updata(mid+1,r,L,R,x<<1|1,val);
    tr[x] = (tr[x<<1] + tr[x<<1|1]);
}
/***************以上为普通线段树****************/
LL queryPath(int x,int y){
    LL sum = 0;
    while(top[x] != top[y]){
        if(d[top[x]] < d[top[y]]) swap(x,y);
        query(1,n,id[top[x]],id[x],1,sum);
        x = f[top[x]];
    }
    if(d[x] > d[y]) swap(x,y);
    query(1,n,id[x],id[y],1,sum);
    return sum;
}
void updataPath(int x,int y,LL val){
     while(top[x] != top[y]){
        if(d[top[x]] < d[top[y]]) swap(x,y);
        updata(1,n,id[top[x]],id[x],1,val);
        x = f[top[x]];
     }
     if(d[x] > d[y]) swap(x,y);
     updata(1,n,id[x],id[y],1,val);   
}
/**************以上为树上两点之间路径的操作****************/
int main(){
    scanf("%d %d",&n,&m);
    for(int i = 1;i <= n; ++i) scanf("%d",a+i);
    for(int i = 1;i < n; ++i) {
        scanf("%d %d",&u,&v);
        add(u,v); add(v,u);
    }
    dfs1(1,0,1); dfs2(1,1); build(1,n,1);
    while(m--){
        scanf("%d",&op);
        if(op == 1){
            scanf("%d %d",&u,&val);
            updata(1,n,id[u],id[u],1,val);
        }
        else if(op == 2){
            scanf("%d %d",&u,&val);
            updataSon(u,val);
        }
        else{
            scanf("%d",&u);
            printf("%lld\n",queryPath(1,u));
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值