HDU - 6962 I love tree(樹鏈剖分, 線段樹)
阿新 • • 發佈:2021-07-29
題目大意
給你一顆樹,有兩種操作。一種是給樹上兩個點之間的點\((x_1, x_2...x_k)\)加上\(1^2, 2^2...k^2\)。另一種是查詢樹上某個節點的值,初始每個節點的值都是\(0\)。
解題思路
題目很明顯可以樹剖之後建線段樹來做區間修改和單點查詢。 給區間加上等差數列的平方可以用加二次函式的形式。對於區間\([l,r]\),設\(k=l-1\),可以對每個數加上\((i-k)^2\),\(i\)是這個元素的下標,但是這樣不具有可加性,可以試著把式子展開,變成\(i^2-2ki+k^2\),將\(i\)提出來,變成\(i^2 \times 1 - 2i \times k + k^2\)
由於樹剖之後兩個結點會同時往上跳,就有了要計算正著加和倒著加的情況,倒著加也很簡單,設\(k\)等於\(r+1\),就變成了對每個數加\((k-i)^2\),翻轉一下變成\(i-k\),發現和前面只是\(k\)的取值不同罷了。
const int maxn = 2e5+10; const int maxm = 1e7+10; int n, q; vector<int> e[maxn]; int dep[maxn], fa[maxn], sz[maxn], son[maxn]; void dfs1(int u, int p) { sz[u] = 1; for (auto v : e[u]) { if (v==p) continue; dep[v] = dep[u]+1; fa[v] = u; dfs1(v, u); sz[u] += sz[v]; if (sz[v]>sz[son[u]]) son[u] = v; } } int top[maxn], tim, id[maxn], rev[maxn]; void dfs2(int u, int t) { top[u] = t; id[u] = ++tim; rev[tim] = u; if (!son[u]) return; dfs2(son[u], t); for (auto v : e[u]) if (v!=fa[u] && v!=son[u]) dfs2(v, v); } struct Node { ll lz1, lz2, lz3; } tr[maxn<<2]; inline void push_down(int rt) { if (tr[rt].lz1) { Node &f = tr[rt]; Node &ls = tr[rt<<1]; Node &rs = tr[rt<<1|1]; ls.lz1 += f.lz1; ls.lz2 += f.lz2; ls.lz3 += f.lz3; rs.lz1 += f.lz1; rs.lz2 += f.lz2; rs.lz3 += f.lz3; f.lz1 = f.lz2 = f.lz3 = 0; } } void update(int rt, int l, int r, int L, int R, ll val) { if (l>=L && r<=R) { tr[rt].lz1 += 1; tr[rt].lz2 += val*val; tr[rt].lz3 += val; return; } push_down(rt); int mid = (l+r)>>1; if (L<=mid) update(rt<<1, l, mid, L, R, val); if (R>mid) update(rt<<1|1, mid+1, r, L, R, val); } Node ask(int rt, int l, int r, int pos) { if (l==r) return tr[rt]; push_down(rt); int mid = (l+r)>>1; if (pos<=mid) return ask(rt<<1, l, mid, pos); else return ask(rt<<1|1, mid+1, r, pos); } int lca(int u, int v) { while(top[u]!=top[v]) { if (dep[top[u]]>dep[top[v]]) u = fa[top[u]]; else v = fa[top[v]]; } return dep[u]>dep[v] ? v:u; } void modify(int u, int v) { int l = 1, r = dep[u]+dep[v]-2*dep[lca(u, v)]+1; int add = 0; while(top[u]!=top[v]) { if (dep[top[u]]>dep[top[v]]) { //u往上跳,從top[u]到u倒著加 add = id[u]+l; l += id[u]-id[top[u]]; update(1, 1, n, id[top[u]], id[u], add); u = fa[top[u]]; ++l; } else { //v往上跳從top[v]到v正著加 r -= dep[v]-dep[top[v]]; add = id[top[v]]-r; update(1, 1, n, id[top[v]], id[v], add); v = fa[top[v]]; --r; } } if (dep[u]>dep[v]) { add = id[u]+l; update(1, 1, n, id[v], id[u], add); } else { add = id[u]-l; update(1, 1, n, id[u], id[v], add); } } int main() { IOS; cin >> n; for (int i = 1, a, b; i<n; ++i) { cin >> a >> b; e[a].push_back(b); e[b].push_back(a); } dfs1(1, 0); dfs2(1, 1); cin >> q; while(q--) { int o, l, r; cin >> o; if (o==1) { cin >> l >> r; modify(l, r); } else if (o==2) { cin >> l; Node res = ask(1, 1, n, id[l]); ll ans = id[l]*id[l]*res.lz1+res.lz2-2*id[l]*res.lz3; cout << ans << endl; } } return 0; }