1. 程式人生 > 其它 >HDU - 6962 I love tree(樹鏈剖分, 線段樹)

HDU - 6962 I love tree(樹鏈剖分, 線段樹)

題目連結

題目大意

  給你一顆樹,有兩種操作。一種是給樹上兩個點之間的點\((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\)

,我們發現\(1\)\(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;
}