1. 程式人生 > >bzoj 4034: [HAOI2015]樹上操作 (樹剖+線段樹 子樹操作)

bzoj 4034: [HAOI2015]樹上操作 (樹剖+線段樹 子樹操作)

size define next dfs ans n-1 兩個 center mit

4034: [HAOI2015]樹上操作

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 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

Sample Output

6
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]樹上操作 (樹剖+線段樹 子樹操作)