luogu P3178 [HAOI2015]树上操作

题目

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

  • 操作 1 :把某个节点 x 的点权增加 a 。
  • 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
  • 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

输入格式

第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

输出格式

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

输入输出样例

输入 #1复制

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

输出 #1复制

6
9
13

说明/提示

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

分析

  • 首先,我们要知道dfs序
  • 就是一颗树进出栈的顺序
  • 然后我们记录下他们的时间戳
  • 就知道他们的左右子树在哪个区间
  • 然后用线段树维护
  • 还有一个重要的点是
  • 我们可以发现1-x点就是x进栈的前缀
  • 考虑如何维护线段树
  • 首先我们是有x和-x的,我们要一个前缀数组搞出区间正x的个数
  • 对于第一个修改x+=x -x+=-x就好了
  • 然后主要区间加一定打lazy
  • lazy的return 是如果数在区间里面的
  • 然后还有

    if (a<=mid) ans+=find(k<<1,a,b);
    if (b>=mid+1) ans+=find(k<<1|1,a,b); 别忘了

代码

  1 #include<iostream>
  2 #include<cstdio>
  3 #define ll long long
  4 using namespace std;
  5 ll a[500001];
  6 struct sb
  7 {
  8     ll l,r,sum,bz,add;
  9 }t[2*200001];
 10 ll id[500001],num,le[500001],ri[500001],vis[500001],ss[500001];
 11 struct node
 12 {
 13     ll to,nx;
 14 }g[2*200010];
 15 ll list[2*200010],cnt;
 16 void add(ll x,ll y)
 17 {
 18     g[++cnt].to=y; g[cnt].nx=list[x]; list[x]=cnt;
 19     g[++cnt].to=x; g[cnt].nx=list[y]; list[y]=cnt;
 20 }
 21 ll val[500001],v,flag[500001];
 22 inline ll read(){
 23     ll x=0,f=1;
 24     char ch=getchar();
 25     while(ch<‘0‘||ch>‘9‘){
 26         if(ch==‘-‘)
 27             f=-1;
 28         ch=getchar();
 29     }
 30     while(ch>=‘0‘&&ch<=‘9‘){
 31         x=(x<<1)+(x<<3)+(ch^48);
 32         ch=getchar();
 33     }
 34     return x*f;
 35 }
 36 void dfs(ll x)
 37 {
 38     vis[x]=1;
 39     id[x]=++num;
 40     val[num]=a[x];
 41     flag[num]=1;
 42     le[x]=num;
 43     for (int i=list[x];i;i=g[i].nx)
 44     {
 45         ll y=g[i].to;
 46         if (!vis[y])
 47             dfs(y);
 48     }
 49     ri[x]=++num;
 50     val[num]=-a[x];
 51     flag[num]=-1;
 52 }
 53 void build(ll k,ll a,ll b)
 54 {
 55     t[k].l=a; t[k].r=b;
 56     if (a==b)
 57     {
 58         t[k].sum=val[a];
 59         t[k].bz=flag[a];
 60         return;
 61     }
 62     ll mid=a+b>>1;
 63     build(k<<1,a,mid);
 64     build(k<<1|1,mid+1,b);
 65     t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
 66     t[k].bz=t[k<<1].bz+t[k<<1|1].bz;
 67 }
 68 void change(ll k,ll mb,ll z)
 69 {
 70     t[k].sum+=z;
 71     if (t[k].l==t[k].r) return;
 72     ll mid=t[k].l+t[k].r>>1;
 73     if (mb<=mid) change(k<<1,mb,z);
 74     else change(k<<1|1,mb,z);
 75 }
 76 void lazy(ll k,ll a,ll b,ll m)
 77 {
 78     if (t[k].add!=0)
 79     {
 80         t[k<<1].add+=t[k].add;
 81         t[k<<1|1].add+=t[k].add;
 82         t[k<<1].sum=t[k<<1].sum+t[k].add*(ss[m]-ss[a-1]);
 83         t[k<<1|1].sum=t[k<<1|1].sum+t[k].add*(ss[b]-ss[m]);
 84         t[k].add=0;
 85     }
 86 }
 87 void change2(ll k,ll a,ll b,ll z)
 88 {
 89     if (a<=t[k].l&&b>=t[k].r)
 90     {
 91         t[k].add+=z;
 92         t[k].sum=t[k].sum+z*(ss[t[k].r]-ss[t[k].l-1]);
 93         return;
 94     }
 95     ll mid=(t[k].l+t[k].r)>>1;
 96     lazy(k,t[k].l,t[k].r,mid);
 97     if (a<=mid) change2(k<<1,a,b,z);
 98     if (b>=mid+1) change2(k<<1|1,a,b,z);
 99     t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
100 }
101 ll find(ll k,ll a,ll b)
102 {
103     if (t[k].l>=a&&t[k].r<=b)
104       return t[k].sum;
105     ll mid=t[k].l+t[k].r>>1;
106     ll ans=0;
107     lazy(k,t[k].l,t[k].r,mid);
108     if (a<=mid) ans+=find(k<<1,a,b);
109     if (b>=mid+1) ans+=find(k<<1|1,a,b);
110     return ans;
111 }
112 int main ()
113 {
114     ll n,m;
115     n=read(); m=read();
116     for (int i=1;i<=n;i++)
117       a[i]=read();
118     for (int i=1,x,y;i<=n-1;i++)
119     {
120         x=read(); y=read();
121         add(x,y);
122     }
123     dfs(1);
124     for (int i=1;i<=num;i++)
125       ss[i]=ss[i-1]+flag[i];
126     build(1,1,2*n);
127     for (int i=1,op,x,y;i<=m;i++)
128     {
129         op=read();
130         if (op==1)
131         {
132             x=read(); y=read();
133             change(1,le[x],y);
134             change(1,ri[x],-y);
135         }
136         if (op==2)
137         {
138             x=read(); y=read();
139             change2(1,le[x],ri[x],y);
140         }
141         if (op==3)
142         {
143             x=read();
144             printf("%lld\n",find(1,1,le[x]));
145         }
146     }
147 }

原文地址:https://www.cnblogs.com/zjzjzj/p/11379377.html

时间: 2024-10-07 04:51:38

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

P3178 [HAOI2015]树上操作

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

洛谷——P3178 [HAOI2015]树上操作

https://www.luogu.org/problem/show?pid=3178#sub 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a .操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入输出格式 输入格式: 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1

P3178 [HAOI2015]树上操作 (树链剖分模版题)

题目链接:https://www.luogu.org/problem/P3178 一定要用LL  !!!!! 1 #include <stdio.h> 2 #include <cstring> 3 #include <iostream> 4 #include <string> 5 #include <algorithm> 6 #include <queue> 7 #include <vector> 8 #include &

洛谷P3178 [HAOI2015]树上操作

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

[HAOI2015]树上操作(树链剖分)

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

【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 到根的路径中所有