bzoj 4034: [HAOI2015]树上操作 (树剖+线段树 子树操作)
Posted Dysania_l
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 4034: [HAOI2015]树上操作 (树剖+线段树 子树操作)相关的知识,希望对你有一定的参考价值。
4034: [HAOI2015]树上操作
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 6779 Solved: 2275
[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
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
9
13
HINT
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
思路:
在dfs的时候,加个mx数组,存下每个节点的子节点坐标在线段树中最大的值,那么对以这个点为根节点的子树进行操作是,也就是操作根节点到根节点坐标最大的子节点这段区间,然后就很容易了
。
实现代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mid ll m = (l + r) >> 1 const ll M = 2e5+10; struct node{ ll to,next; }e[M]; ll n,m,cnt1,cnt,a[M]; ll sum[M<<2],lazy[M<<2],son[M],fa[M],head[M],siz[M],top[M],dep[M],tid[M],rk[M],mx[M]; void add(ll u,ll v){ e[++cnt1].to = v;e[cnt1].next = head[u];head[u] = cnt1; e[++cnt1].to = u;e[cnt1].next = head[v];head[v] = cnt1; } void dfs1(ll u,ll faz,ll deep){ dep[u] = deep; fa[u] = faz; siz[u] = 1; for(ll i = head[u];i;i=e[i].next){ ll v = e[i].to; if(v != fa[u]){ dfs1(v,u,deep+1); siz[u] += siz[v]; if(son[u] == -1||siz[v] > siz[son[u]]) son[u] = v; } } } void dfs2(ll u,ll t){ top[u] = t; mx[u] = cnt; tid[u] = cnt; rk[cnt] = u; cnt++; if(son[u] == -1) return; dfs2(son[u],t),mx[u] = max(mx[u],mx[son[u]]); for(ll i = head[u];i;i = e[i].next){ ll v = e[i].to; if(v != son[u]&&v != fa[u]) dfs2(v,v),mx[u]=max(mx[u],mx[v]); } } void pushup(ll rt){ sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void pushdown(ll l,ll r,ll rt){ if(lazy[rt]){ mid; sum[rt<<1] += lazy[rt]*(m-l+1); sum[rt<<1|1] += lazy[rt]*(r - m); lazy[rt<<1] += lazy[rt]; lazy[rt<<1|1] += lazy[rt]; lazy[rt] = 0; } } void build(ll l,ll r,ll rt){ lazy[rt] = 0; if(l == r){ sum[rt] = a[rk[l]]; return ; } mid; build(lson); build(rson); pushup(rt); } void update(ll L,ll R,ll c,ll l,ll r,ll rt){ if(L <= l&&R >= r){ sum[rt] += c*(r-l+1); lazy[rt]+=c; return ; } pushdown(l,r,rt); mid; if(L <= m) update(L,R,c,lson); if(R > m) update(L,R,c,rson); pushup(rt); } ll query(ll L,ll R,ll l,ll r,ll rt){ if(L <= l&&R >= r){ return sum[rt]; } pushdown(l,r,rt); mid; ll ret = 0; if(L <= m) ret += query(L,R,lson); if(R > m) ret += query(L,R,rson); return ret; } ll ask(ll x,ll y){ ll ans = 0; ll fx = top[x],fy = top[y]; while(fx != fy){ if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy); ans += query(tid[fx],tid[x],1,n,1); x = fa[fx];fx=top[x]; } if(dep[x] > dep[y]) swap(x,y); ans += query(tid[x],tid[y],1,n,1); return ans; } int main() { ll u,v,x,y,z; scanf("%lld%lld",&n,&m); cnt1 = 0;cnt = 1; memset(son,-1,sizeof(son)); for(ll i = 1;i <= n;i ++){ scanf("%lld",&a[i]); } for(ll i = 0;i < n-1;i++){ scanf("%lld%lld",&u,&v); add(u,v); } dfs1(1,0,1);dfs2(1,1),build(1,n,1); while(m--){ scanf("%lld%lld",&x,&y); if(x==1){ scanf("%lld",&z); update(tid[y],tid[y],z,1,n,1); } else if(x == 2){ scanf("%lld",&z); update(tid[y],mx[y],z,1,n,1); } else{ printf("%lld\n",ask(1,y)); } } return 0; }
以上是关于bzoj 4034: [HAOI2015]树上操作 (树剖+线段树 子树操作)的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 4034: [HAOI2015]树上操作 (树剖+线段树 子树操作)
bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树