[BZOJ4034][HAOI2015]树上操作


题目描述 Description

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

操作,分为三种:

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

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

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


输入描述 Input Description

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

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

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


输出描述 Output Description

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

样例输入 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

数据范围及提示 Data Size & Hint


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

本题和树链剖分裸题唯一的区别就是本题有修改子树这种操作。不过我们不要慌,再仔细想一想树链剖分能支持做的事情:对一段区间进行修改。很巧的是子树由于DFS的原因就是在一个连续的区间里,于是我们用两个数组l,r表示当前节点对应子树的区间,在divide的时候记录一下即可。

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
typedef long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair<int,int> PII;
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c==‘-‘)f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
const int maxn=100010;
int n,q,cost[maxn],w[maxn],first[maxn],fa[maxn],id[maxn],bl[maxn],size[maxn],a,b,tp,ce=-1,es,l[maxn],r[maxn],maxs[maxn];
LL tag[maxn<<2],sum[maxn<<2];
struct Edge
{
    int u,v,next;
    Edge() {}
    Edge(int _1,int _2,int _3):u(_1),v(_2),next(_3) {}
}e[maxn<<1];
void addEdge(int a,int b)
{
    e[++ce]=Edge(a,b,first[a]);first[a]=ce;
    e[++ce]=Edge(b,a,first[b]);first[b]=ce;
}
void dfs(int now,int pa)
{
    size[now]=1;
    for(int i=first[now];i!=-1;i=e[i].next)
        if(e[i].v!=pa)
        {
            fa[e[i].v]=now;
            dfs(e[i].v,now);
            size[now]+=size[e[i].v];
            if(size[e[i].v]>size[maxs[now]])maxs[now]=e[i].v;
        }
    return;
}
void divide(int now,int chain)
{
    bl[now]=chain;id[now]=++es;l[now]=es;
    if(maxs[now])divide(maxs[now],chain);
    for(int i=first[now];i!=-1;i=e[i].next)if(e[i].v!=fa[now] && e[i].v!=maxs[now])divide(e[i].v,e[i].v);
    r[now]=es;
    return;
}
void pushdown(int l,int r,int o)
{
    int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
    if(l==r){tag[o]=0;return;}
    tag[lo]+=tag[o];tag[ro]+=tag[o];
    sum[lo]+=(LL)(mid-l+1)*tag[o];
    sum[ro]+=(LL)(r-mid)*tag[o];
    tag[o]=0;
}
void build(int l,int r,int o)
{
    if(l==r)
    {
        sum[o]=w[l];
        return;
    }
    int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
    build(l,mid,lo);build(mid+1,r,ro);
    sum[o]=sum[lo]+sum[ro];
    return;
}
void update(int l,int r,int o,int a,int b,int c)
{
    if(l==a && r==b)
    {
        tag[o]+=c;
        sum[o]+=(LL)(r-l+1)*c;
        return;
    }
    pushdown(l,r,o);
    int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
    if(b<=mid)update(l,mid,lo,a,b,c);
    else if(a>mid)update(mid+1,r,ro,a,b,c);
    else update(l,mid,lo,a,mid,c),update(mid+1,r,ro,mid+1,b,c);
    sum[o]=sum[lo]+sum[ro]+tag[o]*(r-l+1);
    return;
}
LL sectionsum(int l,int r,int o,int a,int b)
{
    if(l==a && r==b)return sum[o];
    int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
    pushdown(l,r,o);
    if(b<=mid)return sectionsum(l,mid,lo,a,b);
    else if(a>mid)return sectionsum(mid+1,r,ro,a,b);
    else return sectionsum(l,mid,lo,a,mid)+sectionsum(mid+1,r,ro,mid+1,b);
}
LL query(int a)
{
    LL ans=0;
    while(bl[a]!=1)
    {
        ans+=sectionsum(1,n,1,id[bl[a]],id[a]);
        a=fa[bl[a]];
    }
    ans+=sectionsum(1,n,1,1,id[a]);
    return ans;
}
int main()
{
    mem(first,-1);
    n=read();q=read();
    for(int i=1;i<=n;i++)cost[i]=read();
    for(int i=1;i<n;i++)a=read(),b=read(),addEdge(a,b);
    dfs(1,-1);divide(1,1);
    for(int i=1;i<=n;i++)w[id[i]]=cost[i];
    build(1,n,1);
    while(q--)
    {
        tp=read();a=read();
        if(tp==3)printf("%lld\n",query(a));
        else
        {
            b=read();
            if(tp==1)update(1,n,1,l[a],l[a],b);
            else update(1,n,1,l[a],r[a],b);
        }
    }
    return 0;
}

时间: 2024-10-12 16:42:37

[BZOJ4034][HAOI2015]树上操作的相关文章

bzoj千题计划242:bzoj4034: [HAOI2015]树上操作

http://www.lydsy.com/JudgeOnline/problem.php?id=4034 dfs序,树链剖分 #include<cstdio> #include<iostream> using namespace std; #define N 100001 typedef long long LL; int n,a[N]; int front[N],nxt[N<<1],to[N<<1],tot; int fa[N],siz[N],dep[N]

bzoj4034: [HAOI2015]树上操作(树剖)

4034: [HAOI2015]树上操作 题目:传送门 题解: 树剖裸题: 麻烦一点的就只有子树修改(其实一点也不),因为子树编号连续啊,直接改段(记录编号最小和最大) 开个long long 水模版 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std

【BZOJ4034】[HAOI2015]树上操作 树链剖分+线段树

[BZOJ4034][HAOI2015]树上操作 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数. 接下来一行 N 个整数,表示树中节点的初始权值. 接下来 N-1 行每行三个正整数 fr, to

[HAOI2015]树上操作

[HAOI2015]树上操作 2017-09-07 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行三个正整数 fr, to ,

[HAOI2015]树上操作 -树链剖分

1963. [HAOI2015]树上操作 [题目描述] 有一棵点数为N的树,以点1为根,且树点有权值.然后有M个操作,分为三种: 操作1:把某个节点x的点权增加a. 操作2:把某个节点x为根的子树中所有点的点权都增加a. 操作3:询问某个节点x到根的路径中所有点的点权和. [输入格式] 第一行两个整数N,M,表示点数和操作数. 接下来一行N个整数,表示树中节点的初始权值. 接下来N-1行每行两个正整数fr,to,表示该树中存在一条边(fr,to). 再接下来M行,每行分别表示一次操作.其中第一个

bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4352  Solved: 1387[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有

BZOJ 4034 HAOI2015 树上操作

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 6033  Solved: 1959[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有

4034: [HAOI2015]树上操作

4034: [HAOI2015]树上操作 链接 思路: 树链剖分.操作:单点修改,路径查询,子树修改. 代码: 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 8 using namespace std; 9 10 const int N =

P3178 [HAOI2015]树上操作

P3178 [HAOI2015]树上操作 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a .操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入输出格式 输入格式: 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行两个正整数 from, to , 表示该树中存