HDU-6962 I love tree(树链剖分+线段树)

7 篇文章 0 订阅
3 篇文章 0 订阅

思路:如果是对区间 [ l , r ] [l,r] [l,r]进行操作的话,对于 x ∈ [ l , r ] , w [ x ] + = ( x − l + 1 ) 2 x∈[l,r],w[x]+=(x-l+1)^2 x[l,r],w[x]+=(xl+1)2,

k = l − 1 k=l-1 k=l1,那么 w [ x ] + = ( x − k ) 2 w[x]+=(x-k)^2 w[x]+=(xk)2,把括号拆开 − > x 2 − 2 ∗ x ∗ k + k 2 ->x^2-2*x*k+k^2 >x22xk+k2,

那么就需要维护3个对于 t t t次修改后的线段树 ∑ i = 1 t 1 \sum_{i=1}^{t}{1} i=1t1 ∑ i = 1 t k \sum_{i=1}^{t}{k} i=1tk ∑ i = 1 t k 2 \sum_{i=1}^{t}{k^2} i=1tk2

在查询 x x x时就返回 x 2 ∑ i = 1 t 1 − 2 x ∑ i = 1 t k + ∑ i = 1 t k 2 x^2\sum_{i=1}^{t}{1} -2x\sum_{i=1}^{t}{k}+\sum_{i=1}^{t}{k^2} x2i=1t12xi=1tk+i=1tk2

在树上进行操作需要先树链剖分,把一条路径分成若干条连续区间

要注意的是在modify每个区间时,是此区间的左端点为准

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>

using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
vector<int> v[N];
int n, x, y, q, a, b;
int dep[N], id[N], son[N], idx, sz[N], top[N], f[N], op;

void dfs1(int u, int fa) {
    dep[u] = dep[fa] + 1, sz[u] = 1, f[u] = fa;
    for (auto i:v[u]) {
        if (i == fa)continue;
        dfs1(i, u);
        sz[u] += sz[i];
        if (sz[son[u]] < sz[i])son[u] = i;
    }
}

void dfs2(int u, int t) {
    top[u] = t, id[u] = ++idx;
    if (!son[u])return;
    dfs2(son[u], t);
    for (auto i:v[u]) {
        if (i == f[u] || i == son[u])continue;
        dfs2(i, i);
    }
}

struct segtree {
    struct node {
        ll l, r;
        ll sum, lazy;
    } tr[N << 2];

    void pushup(int u) {
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }

    void pushdown(int u) {
        tr[u << 1].lazy += tr[u].lazy, tr[u << 1 | 1].lazy += tr[u].lazy;
        tr[u << 1].sum += tr[u].lazy * (tr[u << 1].r - tr[u << 1].l + 1);
        tr[u << 1 | 1].sum += tr[u].lazy * (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1);
        tr[u].lazy = 0;
    }

    void build(int u, int l, int r) {
        tr[u] = {l, r, 0, 0};
        if (l == r)return;
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }

    void modify(int u, int l, int r, ll k) {
        if (l <= tr[u].l && r >= tr[u].r) {
            tr[u].sum += k * (tr[u].r - tr[u].l + 1);
            tr[u].lazy += k;
            return;
        }
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid)modify(u << 1, l, r, k);
        if (r > mid)modify(u << 1 | 1, l, r, k);
        pushup(u);
    }

    ll query(int u, int x) {
        if (tr[u].l == tr[u].r && tr[u].l == x)
            return tr[u].sum;
        int mid = tr[u].l + tr[u].r >> 1;
        pushdown(u);
        if (x <= mid)return query(u << 1, x);
        return query(u << 1 | 1, x);
    }
} s1, s2, s3;

int lca(int a, int b) {
    while (top[a] != top[b]) {
        if (dep[top[a]] >= dep[top[b]])a = f[top[a]];
        else b = f[top[b]];
    }
    if (dep[a] < dep[b])return a;
    return b;
}

void modify_path(int a, int b) {
    int p = lca(a, b);
    int l = 1, r = dep[a] + dep[b] - 2 * dep[p] + 1;
    while (top[a] != top[b]) {
        if (dep[top[a]] > dep[top[b]]) {
            ll add = id[a] + l;
            l += dep[a] - dep[top[a]] + 1;
            s1.modify(1, id[top[a]], id[a], 1);
            s2.modify(1, id[top[a]], id[a], add);
            s3.modify(1, id[top[a]], id[a], add * add);
            a = f[top[a]];
        } else {
            int len = dep[b] - dep[top[b]] + 1;
            ll add = id[top[b]] - (r - len - 1);
            r -= len;
            s1.modify(1, id[top[b]], id[b], 1);
            s2.modify(1, id[top[b]], id[b], add);
            s3.modify(1, id[top[b]], id[b], add * add);
            b = f[top[b]];
        }
    }
    if (dep[a] > dep[b]) {
        ll add = id[a] + l;
        s1.modify(1, id[b], id[a], 1);
        s2.modify(1, id[b], id[a], add);
        s3.modify(1, id[b], id[a], add * add);
    } else {
        int len = dep[b] - dep[a] + 1;
        ll add = id[a] - (r - len + 1);
        s1.modify(1, id[a], id[b], 1);
        s2.modify(1, id[a], id[b], add);
        s3.modify(1, id[a], id[b], add * add);
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        scanf("%d%d", &a, &b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    dfs1(1, -1);
    dfs2(1, 1);
    s1.build(1, 1, n), s2.build(1, 1, n), s3.build(1, 1, n);
    scanf("%d", &q);
    while (q--) {
        scanf("%d%d", &op, &a);
        if (op == 1) {
            scanf("%d", &b);
            modify_path(a, b);
        } else {
            ll x = s1.query(1, id[a]);
            ll y = s2.query(1, id[a]);
            ll z = s3.query(1, id[a]);
            printf("%lld\n", z - 2ll * id[a] * y + x * id[a] * id[a]);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值