[BZOJ3730][震波][动态树分治+线段树+LCA]

8 篇文章 0 订阅
6 篇文章 0 订阅

[BZOJ3730][震波][动态树分治+线段树+LCA]

题目大意:

给定一棵树,要求支持两种操作:

操作描述
(0,x,k) 求所有和节点x距离不超过K的节点权值和
(1,x,y) 将节点x的权值修改为y
思路:

树分治题,首先求出每个重心,对重心连边形成点分树。然后在每个重心上动态开两颗线段树。用在x位置增加y表示距离重心x的位置有一个权值为y的点。其中第一棵线段树用于统计答案,第二棵线段树用于在统计父节点时消除子树对它的影响。


G

对于如图所示的这样一棵树,其中实线的是树边,虚线的是重心之间的连边,我们统计距离2号节点长度不超过3的所有节点权值和,初始时 dist=3 。首先在重心2管辖的节点内统计 d<=dist=3 的节点共有6个为 0,2,3,4,5,6 ,然后向上爬至重心7, dist=1 (减去两个重心之间的距离)。在重心7管辖的节点内统计 d<=dist=1 的节点共有3个为 0,7,8 ,同时在重心2的第二棵线段树上减去位于重心2所在子树上的节点0。

总共统计到的节点为: 0,2,3,4,5,6,7,8


每次修改时从节点x开始暴力爬树高,对经过的每个重心的线段树进行修改。

每次统计的时候也是暴力爬树高,在经过的每个重心的线段树里进行统计。

由于点分树树高 logn ,干什么事都不虚!

总复杂度 O((n+m)log2n)

代码:

4KB+的代码调试起来真的爽~


#include <cstdio>
const int Maxn = 500100;
inline void swap(int &x, int &y) {
    static int tmp;
    tmp = x;
    x = y;
    y = tmp;
}
inline int Max(const int &a, const int &b) {
    return a > b ? a : b;
}
inline char get(void) {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; static char c; bool minus = false;
    for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') minus = true;
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (minus) x = -x;
}
inline void write(int x) {
    if (!x) return (void) puts("0");
    if (x < 0) putchar('-'), x = -x;
    static short s[12], t;
    while (x) s[++t] = x % 10, x /= 10;
    while (t) putchar('0' + s[t--]);
    putchar('\n');
}
int n, m, ans;
int head[Maxn], sub;
struct Edge {
    int to, nxt;
    Edge (void) {}
    Edge (const int &to, const int &nxt) : to(to), nxt(nxt) {}
} edge[Maxn << 1];

inline void add(int a, int b) {
    edge[++sub] = Edge(b, head[a]), head[a] = sub;
}
int dep[Maxn], ff[Maxn][25];
inline void dfs1(int u, int fa) {
    ff[u][0] = fa;
    for (int i = 1; i <= 20; i++) {
        ff[u][i] = ff[ff[u][i - 1]][i - 1];
    }
    for (int i = head[u], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (v == fa) continue;
        dep[v] = dep[u] + 1;
        dfs1(v, u);
    }
}
inline int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    int tmp = dep[x] - dep[y];
    for (int k = 0, j = 1; j <= tmp; j <<= 1, k++)
            if (tmp & j) x = ff[x][k];
    while (x != y) {
        int j = 0;
        while (ff[x][j] != ff[y][j]) j++;
        if (j) j--;
        x = ff[x][j], y = ff[y][j];
    }
    return x;
}
inline int dist(int x, int y) {
    return dep[x] + dep[y] - (dep[lca(x, y)] << 1);
}
int s[Maxn << 4], sz, ls[Maxn << 4], rs[Maxn << 4], root[Maxn][2];
inline void insert(int &k, int l, int r, int v, int p) {
    if (!k) k = ++sz; s[k] += p;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (mid >= v) insert(ls[k], l, mid, v, p);
    else insert(rs[k], mid + 1, r, v, p);
}
inline int query(int k, int l, int r, int L, int R) {
    if (!k) return 0;
    if (l >= L && r <= R) return s[k];
    int mid = (l + r) >> 1;
    if (R <= mid) return query(ls[k], l, mid, L, R);
    else if (L > mid) return query(rs[k], mid + 1, r, L, R);
    else return query(ls[k], l, mid, L, mid) + query(rs[k], mid + 1, r, mid + 1, R);
}
bool vis[Maxn];
int d[Maxn], v[Maxn], size, son[Maxn], rnt, mson[Maxn], p[Maxn];
inline void run(int rt, int l, int u, int fa) {
    insert(root[rt][l], 0, n, d[u], v[u]);
    for (int i = head[u], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (v == fa || vis[v]) continue;
        d[v] = d[u] + 1; 
        run(rt, l, v, u);
    }
}
inline void getroot(int u, int fa) {
    son[u] = 1; mson[u] = 0;
    for (int i = head[u], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (v == fa || vis[v]) continue;
        getroot(v, u);
        son[u] += son[v];
        mson[u] = Max(mson[u], son[v]);
    }
    mson[u] = Max(mson[u], size - son[u]);
    if (mson[u] < mson[rnt]) rnt = u;
}
inline void work(int u) {
    vis[u] = 1; d[u] = 0; run(u, 0, u, 0);
    for (int i = head[u], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (vis[v]) continue;
        size = son[v]; rnt = 0;
        d[v] = 1;
        getroot(v, 0); run(rnt, 1, v, u);
        p[rnt] = u;
        work(rnt);
    }
}
inline int ask(int u, int k) {
    int ret = query(root[u][0], 0, n, 0, k);
    for (int i = u; p[i]; i = p[i]) {
        int cn = dist(u, p[i]);
        ret += query(root[p[i]][0], 0, n, 0, k - cn);
        ret -= query(root[i][1], 0, n, 0, k - cn);
    }
    return ret;
}
inline void update(int u, int k) {
    int sub = k - query(root[u][0], 0, n, 0, 0);
    insert(root[u][0], 0, n, 0, sub);
    for (int i = u; p[i]; i = p[i]) {
        int cn = dist(u, p[i]);
        insert(root[p[i]][0], 0, n, cn, sub);
        insert(root[i][1], 0, n, cn, sub);
    }
}
int main(void) {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    read(n), read(m);
    for (int i = 1; i <= n; i++) read(v[i]);
    for (int i = 1, u, v; i < n; i++) {
        read(u), read(v);
        add(u, v), add(v, u);
    }
    size = mson[0] = n;
    dfs1(1, 0);
    getroot(1, 0); 
    work(rnt);

    for (int i = 1, op, x, y; i <= m; i++) {
        read(op), read(x), read(y);
        x ^= ans, y ^= ans;
        if (op) update(x, y);
        else write(ans = ask(x, y));
    }
    return 0;
}

完。

By g1n0st

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值