[BZOJ4817][SDOI2017]树点涂色(DFS序+LCA+树剖+LCT)

我一眼以为是裸树剖,但是看完问题就知道不是那么容易了。因为只用树剖不能很好地维护操作 3 的结果。
首先,按照原树,构建出一个全部都是虚边的LCT,并用树剖维护每个点到根节点的路径权值 val 。可以发现,每个点到根节点的路径权值就是每个点到根节点的路径上实链的个数。
我们发现,操作 1 实际上就是LCT Access 的操作。在 Access 的操作中,如果一条实边变成虚边,那么将连接这条边的深度较大的节点的子树里所有点的 val 1 (因为实链数量就等于虚边数量+1),如果一条虚边变成实边,那么将连接这条边的深度较大的节点的子树里所有点的 val 1
这样,询问2其实就是求 val[x]+val[y]val[lca(x,y)]2+1 的值,询问 3 就是求x的子树最大值。在以上,由于同一个子树里的节点 DFS 序是一段连续的区间,所以对于子树的修改询问,直接提取对应的 DFS 序区间即可。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, LogN = 24;
int n, m, ecnt, nxt[N << 1], adj[N], go[N << 1], fa_s[N], lc[N], rc[N],
QAQ, fa[N][LogN], dep[N], sze[N], son[N], top[N], pos[N], idx[N], T[N << 2],
add[N << 2], ml[N];
void add_edge(int u, int v) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
int which(int x) {return rc[fa_s[x]] == x;}
bool is_root(int x) {
    return !fa_s[x] || (lc[fa_s[x]] != x && rc[fa_s[x]] != x);
}
void upd(int x) {
    ml[x] = lc[x] ? ml[lc[x]] : x;
}
void rotate(int x) {
    int y = fa_s[x], z = fa_s[y], b = lc[y] == x ? rc[x] : lc[x];
    if (z && !is_root(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
    fa_s[x] = z; fa_s[y] = x; if (b) fa_s[b] = y;
    if (lc[y] == x) rc[x] = y, lc[y] = b;
    else lc[x] = y, rc[y] = b; upd(y); upd(x);
}
void splay(int x) {
    while (!is_root(x)) {
        if (!is_root(fa_s[x])) {
            if (which(x) == which(fa_s[x])) rotate(fa_s[x]);
            else rotate(x);
        }
        rotate(x);
    }
    upd(x);
}
void dfs1(int u, int fu) {
    int i; fa[u][0] = fu; dep[u] = dep[fu] + 1; sze[u] = 1;
    for (i = 0; i <= 19; i++) fa[u][i + 1] = fa[fa[u][i]][i];
    for (int e = adj[u], v; e; e = nxt[e]) {
        if ((v = go[e]) == fu) continue;
        dfs1(v, u); sze[u] += sze[v];
        if (sze[v] > sze[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int fu) {
    if (son[u]) {
        top[son[u]] = top[u];
        idx[pos[son[u]] = ++QAQ] = son[u];
        dfs2(son[u], u);
    }
    for (int e = adj[u], v; e; e = nxt[e]) {
        if ((v = go[e]) == fu || v == son[u]) continue;
        top[v] = v; idx[pos[v] = ++QAQ] = v; dfs2(v, u);
    }
}
void build(int l, int r, int p) {
    if (l == r) return (void) (T[p] = dep[idx[l]]);
    int mid = l + r >> 1;
    build(l, mid, p2); build(mid + 1, r, p3);
    T[p] = max(T[p2], T[p3]);
}
void down(int p) {
    add[p2] += add[p]; add[p3] += add[p]; add[p] = 0;
}
void upt(int p) {
    T[p] = max(T[p2] + add[p2], T[p3] + add[p3]);
}
void change(int l, int r, int s, int e, int v, int p) {
    if (l == s && r == e) return (void) (add[p] += v);
    int mid = l + r >> 1; down(p);
    if (e <= mid) change(l, mid, s, e, v, p2);
    else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
    else change(l, mid, s, mid, v, p2),
        change(mid + 1, r, mid + 1, e, v, p3);
    upt(p);
}
int query(int l, int r, int s, int e, int p) {
    if (l == s && r == e) return T[p] + add[p];
    int mid = l + r >> 1, res = 0; down(p);
    if (e <= mid) res = query(l, mid, s, e, p2);
    else if (s >= mid + 1) res = query(mid + 1, r, s, e, p3);
    else res = max(query(l, mid, s, mid, p2),
        query(mid + 1, r, mid + 1, e, p3));
    upt(p); return res;
}
void init() {
    int i; QAQ = top[1] = pos[1] = idx[1] = 1;
    dfs1(1, 0); dfs2(1, 0); build(1, n, 1);
    for (i = 1; i <= n; i++) fa_s[i] = fa[i][0], ml[i] = i;
}
void Access(int x) {
    int y;
    for (y = 0; x; y = x, x = fa_s[x]) {
        splay(x);
        if (rc[x]) change(1, n, pos[ml[rc[x]]], pos[ml[rc[x]]] +
            sze[ml[rc[x]]]- 1, 1, 1);
        if (y) change(1, n, pos[ml[y]], pos[ml[y]] + sze[ml[y]] - 1, -1, 1);
        rc[x] = y; if (y) fa_s[y] = x;
    }
}
int lca(int u, int v) {
    int i; if (dep[u] < dep[v]) swap(u, v);
    for (i = 20; i >= 0; i--) {
        if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
        if (u == v) return u;
    }
    for (i = 20; i >= 0; i--) if (fa[u][i] != fa[v][i])
        u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}
int path_query(int u, int v) {
    int w = lca(u, v), res;
    res = query(1, n, pos[u], pos[u], 1) +
        query(1, n, pos[v], pos[v], 1);
    return res - (query(1, n, pos[w], pos[w], 1) << 1) + 1;
}
int main() {
    int i, op, x, y; n = read(); m = read();
    for (i = 1; i < n; i++) {
        x = read(); y = read();
        add_edge(x, y); add_edge(y, x);
    }
    init(); while (m--) {
        op = read(); x = read();
        if (op == 1) Access(x);
        else if (op == 2) y = read(), printf("%d\n", path_query(x, y));
        else printf("%d\n", query(1, n, pos[x], pos[x] + sze[x] - 1, 1));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值