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 }
为何要逼自己长大,去闯不该闯的荒唐