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

Description

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

题解

显然,树链剖分的序也是DFS序,直接维护就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define LL long long
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline LL _read(){
    char ch=nc();LL sum=0,p=1;
    while((ch!='-')&&(!(ch>='0'&&ch<='9')))ch=nc();
    if(ch=='-')p=-1,ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*p;
}
struct data{
    int l,r;
    LL sum,tag;
    void add(LL k){
        sum+=k*(r-l+1);tag+=k;
    }
}tree[maxn*4];
int n,T,tot,tem,top[maxn],fa[maxn],id[maxn],num[maxn],dep[maxn],H_son[maxn],lnk[maxn],son[maxn*2],nxt[maxn*2];
LL a[maxn];
bool vis[maxn];
void add(int x,int y){
    nxt[++tot]=lnk[x];son[tot]=y;lnk[x]=tot;
}
void pushdown(int p){
    if(tree[p].tag==0)return;
    tree[p<<1].add(tree[p].tag);tree[p<<1|1].add(tree[p].tag);
    tree[p].tag=0;
}
void build(int p,int l,int r){
    tree[p].l=l;tree[p].r=r;
    if(l>=r)return;
    int mid=(l+r)>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
void update(int p,int l,int r,LL k){
    if(tree[p].r<l||tree[p].l>r)return;
    if(l<=tree[p].l&&r>=tree[p].r){
        tree[p].add(k);return;
    }
    pushdown(p);
    update(p<<1,l,r,k);update(p<<1|1,l,r,k);
    tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}
LL query(int p,int l,int r){
    if(tree[p].r<l||tree[p].l>r)return 0;
    if(l<=tree[p].l&&r>=tree[p].r)return tree[p].sum;
    pushdown(p);
    return query(p<<1,l,r)+query(p<<1|1,l,r);
}
void dfs1(int x){
    vis[x]=0;num[x]=1;
    for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]]){
        dep[son[j]]=dep[x]+1;fa[son[j]]=x;dfs1(son[j]);
        num[x]+=num[son[j]];if(num[son[j]]>num[H_son[x]])H_son[x]=son[j];
    }
}
void dfs2(int x,int y){
    vis[x]=0;top[x]=y;id[x]=++tem;update(1,tem,tem,a[x]);
    if(vis[H_son[x]]&&H_son[x]>0)dfs2(H_son[x],y);
    for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]]&&son[j]!=H_son[x])dfs2(son[j],son[j]);
}
LL get(int x,int y){
    LL sum=0;
    for(;top[x]!=top[y];x=fa[top[x]]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        sum+=query(1,id[top[x]],id[x]);
    }
    if(id[x]>id[y])swap(x,y);
    return sum+query(1,id[x],id[y]);
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=_read();T=_read();
    build(1,1,n);
    for(int i=1;i<=n;i++)a[i]=_read();
    for(int i=1,x,y;i<n;i++)x=_read(),y=_read(),add(x,y),add(y,x);
    memset(vis,1,sizeof(vis));dfs1(1);
    memset(vis,1,sizeof(vis));dfs2(1,1);
    while(T--){
        int k=_read(),x=_read(),y;
        if(k==1)y=_read(),update(1,id[x],id[x],y);else
        if(k==2)y=_read(),update(1,id[x],id[x]+num[x]-1,y);else
        if(k==3)printf("%lld\n",get(x,1));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值