BZOJ 4034 :[HAOI2015]树上操作 树链剖分裸题

4034: [HAOI2015]树上操作

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 4744 Solved: 1513
[Submit][Status][Discuss]


Description

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个

操作,分为三种:

操作 1 :把某个节点 x 的点权增加 a 。

操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。

操作 3 :询问某个节点 x 到根的路径中所有点的点权和。


Input

第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1

行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中

第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。


Output

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。


Sample Input

5 5

1 2 3 4 5

1 2

1 4

2 3

2 5

3 3

1 2 1

3 5

2 1 2

3 3


Sample Output

6

9

13


HINT

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。



这道题显然是一道树链剖分裸题,有一点值得注意,就是第二个操作的处理,其实我们可以发现,对于一棵子树的修改,我们可以发现,这棵子树中所有点的序号是连续的,所以我们可以记录一个该子树中序号最大的点,就可以进行操作了


#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#define dnt long long
const int MAXN = 100000+10;

using namespace std;

int readin(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
dnt val[MAXN];
struct Line{
    int from,to,nxt;
}line[MAXN*2+10];
int N,M,tail;
int head[MAXN];
int mx[MAXN],top[MAXN],dep[MAXN],siz[MAXN],son[MAXN],fa[MAXN],tid[MAXN],timer,rank[MAXN];
dnt sum[MAXN*4+10],mark[MAXN*4+10];
void add_line(int from,int to){
    tail++;
    line[tail].from=from;
    line[tail].to=to;
    line[tail].nxt=head[from];
    head[from]=tail;
}
void dfs1(int u,int father,int d){
    fa[u]=father;
    dep[u]=d;
    siz[u]=1;
    for(register int i=head[u];i;i=line[i].nxt){
        int v=line[i].to;
        if(v!=father){
            dfs1(v,u,d+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
        }
    }
}
void dfs2(int u,int tp){
    tid[u]=mx[u]=++timer;
    rank[tid[u]]=u;
    top[u]=tp;
    if(son[u]==-1) return;
    dfs2(son[u],tp);mx[u]=max(mx[u],mx[son[u]]);
    for(register int i=head[u];i;i=line[i].nxt){
        int v=line[i].to;
        if(v!=son[u]&&v!=fa[u]){
            dfs2(v,v);
            mx[u]=max(mx[u],mx[v]);
        }
    }
}

void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void pushdown(int rt,int len){
    if(mark[rt]){
        mark[rt<<1]+=mark[rt];
        mark[rt<<1|1]+=mark[rt];
        sum[rt<<1]+=(len-(len>>1))*mark[rt];
        sum[rt<<1|1]+=(len>>1)*mark[rt];
        mark[rt]=0;
    }
}

void build(int l,int r,int rt){
    mark[rt]=0;
    if(l==r){
        sum[rt]=val[rank[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void modify(int L,int R,dnt add,int l,int r,int rt){
    if(L<=l&&R>=r){
        mark[rt]+=add;
        sum[rt]+=(r-l+1)*add;
        return;
    }
    pushdown(rt,r-l+1);
    int mid=(l+r)>>1;
    if(L<=mid) modify(L,R,add,l,mid,rt<<1);
    if(R>mid)  modify(L,R,add,mid+1,r,rt<<1|1); 
    pushup(rt);
}

dnt query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        return sum[rt];
    }
    pushdown(rt,r-l+1);
    int mid=(l+r)>>1;
    dnt sumn=0;
    if(L<=mid) sumn+=query(L,R,l,mid,rt<<1);
    if(R>mid)  sumn+=query(L,R,mid+1,r,rt<<1|1);
    return sumn; 
}

dnt _query(int from,int to){
    dnt ans=0;
    while(top[from]!=top[to]){
        if(dep[top[from]]<dep[top[to]]) swap(from,to);
        ans+=query(tid[top[from]],tid[from],1,N,1);
        from=fa[top[from]];
    }
    if(dep[from]<dep[to]) swap(from,to);
    ans+=query(tid[to],tid[from],1,N,1);
    return ans;
}

int main(){
    freopen("BZOJ4034.txt","r",stdin);
    //freopen(".out","w",stdout);
    int f,t;
    memset(son,-1,sizeof(son));
    scanf("%d%d",&N,&M);
    for(register int i=1;i<=N;i++) scanf("%lld",&val[i]);
    for(register int i=1;i<=N-1;i++){
        scanf("%d%d",&f,&t);
        add_line(f,t);
        add_line(t,f);
    }
    dfs1(1,0,0);
    dfs2(1,1);
    build(1,N,1);
    for(register int i=1;i<=M;i++){
        int opt,te;
        dnt mp;
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%lld",&te,&mp);
            modify(tid[te],tid[te],mp,1,N,1);
        }else if(opt==2){
            scanf("%d%lld",&te,&mp);
            modify(tid[te],mx[te],mp,1,N,1);
        }else{
            scanf("%d",&te);
            mp=_query(1,te);
            printf("%lld\n",mp);
        }
    }
    return 0;
} 

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值