1. 程式人生 > >bzoj4034: [HAOI2015]樹上操作(樹鏈剖分)

bzoj4034: [HAOI2015]樹上操作(樹鏈剖分)

bzoj4034

題目描述:給定一個n個點的樹,有m次操作,操作分3種。

                 1、將某個節點的點權增加x。

                 2、將以某個節點x為根的子樹中所有點的權值加上a。

                 3、詢問某個節點x到根節點的所有點權和。

 

輸入格式:第一行兩個整數,表示樹的節點個數n和操作個數m。

                 第二行n個整數,表示每個節點的權值。

                 接下來n - 1行每行兩個整數,描述這棵樹。

                 接下來m行描述了m個操作。

 

輸出格式:對於每個詢問操作,輸出每行一個整數表示點權和。

 

輸入樣例:

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

 

輸出樣例:

6
9
13

 

解析:又是一道樹剖的模板題,直接上樹剖就好了。

 

程式碼如下:

  1 #include<cstdio>
  2 #include<vector>
  3 #define ll long long
  4 #define lc o << 1
  5 #define
rc o << 1 | 1 6 using namespace std; 7 8 const int maxn = 1e5 + 5; 9 int n, m, val[maxn]; 10 ll sum[maxn * 4], bj[maxn * 4]; 11 int dep[maxn], fa[maxn], top[maxn], size[maxn], seq[maxn], cnt, dfn[maxn], heavy[maxn]; 12 vector <int> ve[maxn]; 13 14 int read(void) { 15 char c; while (c = getchar(), (c < '0' || c > '9') && c != '-'); int x = 0, y = 1; 16 if (c == '-') y = -1; else x = c - '0'; 17 while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x * y; 18 } 19 20 void dfs1(int u, int pre) { 21 dep[u] = dep[pre] + 1; 22 fa[u] = pre; size[u] = 1; 23 for (int i = 0; i < ve[u].size(); ++ i) { 24 int v = ve[u][i]; 25 if (v == pre) continue; 26 dfs1(v, u); 27 size[u] += size[v]; 28 if (size[v] > size[heavy[u]]) heavy[u] = v; 29 } 30 } 31 32 void dfs2(int u, int cur) { 33 top[u] = cur; dfn[u] = ++ cnt; 34 seq[cnt] = u; 35 if (!heavy[u]) return; 36 dfs2(heavy[u], cur); 37 for (int i = 0; i < ve[u].size(); ++ i) { 38 int v = ve[u][i]; 39 if (v == fa[u] || v == heavy[u]) continue; 40 dfs2(v, v); 41 } 42 } 43 44 void build(int o, int l, int r) { 45 if (l == r) { 46 sum[o] = val[seq[l]]; return; 47 } 48 int mid = l + r >> 1; 49 build(lc, l, mid); build(rc, mid + 1, r); 50 sum[o] = sum[lc] + sum[rc]; 51 } 52 53 void pushdown(int o, int l, int r) { 54 int mid = l + r >> 1; 55 sum[lc] += bj[o] * (mid - l + 1); 56 sum[rc] += bj[o] * (r - mid); 57 bj[lc] += bj[o]; bj[rc] += bj[o]; bj[o] = 0; 58 } 59 60 void modify(int o, int l, int r, int ql, int qr, int c) { 61 if (ql <= l && qr >= r) { 62 sum[o] += 1ll * c * (r - l + 1); bj[o] += c; 63 return; 64 } 65 int mid = l + r >> 1; 66 if (bj[o]) pushdown(o, l, r); 67 if (ql <= mid) modify(lc, l, mid, ql, qr, c); 68 if (qr > mid) modify(rc, mid + 1, r, ql, qr, c); 69 sum[o] = sum[lc] + sum[rc]; 70 } 71 72 ll query(int o, int l, int r, int ql, int qr) { 73 if (ql <= l && qr >= r) return sum[o]; 74 int mid = l + r >> 1; ll ans = 0; 75 if (bj[o]) pushdown(o, l, r); 76 if (ql <= mid) ans += query(lc, l, mid, ql, qr); 77 if (qr > mid) ans += query(rc, mid + 1, r, ql, qr); 78 return ans; 79 } 80 81 ll chain_query(int x, int y) { 82 int fax = top[x], fay = top[y]; 83 ll ans = 0; 84 while (fax != fay) { 85 if (dep[fax] < dep[fay]) { 86 swap(x, y); 87 swap(fax, fay); 88 } 89 ans += query(1, 1, n, dfn[fax], dfn[x]); 90 x = fa[fax]; 91 fax = top[x]; 92 } 93 if (dep[x] > dep[y]) swap(x, y); 94 ans += query(1, 1, n, dfn[x], dfn[y]); 95 return ans; 96 } 97 98 int main() { 99 n = read(); m = read(); 100 for (int i = 1; i <= n; ++ i) val[i] = read(); 101 for (int i = 1; i < n; ++ i) { 102 int x = read(), y = read(); 103 ve[x].push_back(y); 104 ve[y].push_back(x); 105 } 106 dfs1(1, 0); 107 dfs2(1, 1); 108 build(1, 1, n); 109 while (m --) { 110 int opt = read(); 111 if (opt == 1) { //單點加a 112 int x = read(), a = read(); 113 modify(1, 1, n, dfn[x], dfn[x], a); 114 } 115 else if (opt == 2) { 116 int x = read(), a = read(); //所有子樹加a 117 modify(1, 1, n, dfn[x], dfn[x] + size[x] - 1, a); 118 } 119 else { 120 int x = read(); //詢問點到根的點權和 121 printf("%lld\n", chain_query(1, x)); 122 } 123 } 124 return 0; 125 }