「Gym 102759I」Query On A Tree 17
题目大意
给定一颗 \(N\) 个节点以 \(1\) 为根的有根树,每次给以 \(u\) 为根的子树每点加 \(1\) 的值或给路径 \(u - v\) 上每点加 \(1\) 的值,每次修改后查询一个点 \(u\) 使得 \(\sum_{v = 1}^N dis(u, v)\) 最小。
题目转换
首先我们很贪心地想到点 \(u\) 是树的带权重心,证明如下:
假设点 \(u\) 是带权重心,\(sum_u\) 为 \(u\) 的子树权值和,另取一点 \(v (v \neq u)\),令 \(u\) 为根,则对在路径 \(u - v\) 上每一点 \(p (p \neq u)\) 有
所以从 \(u\) 到 \(v\) 的移动过程中每一步都不优,故 \(u\) 为我们的答案。
所以我们每次加子树,加路径,查带权重心。
然后再原树上带权重心 \(u\) 的子树权值和一定刚好严格大于整棵树权值和一半,否则一定不优,证明同上。换做 dfs 序,即一个区间权值和大于数列和一半。
做法详解
现在我们明确一下我们要求什么:找到一个深度最深的点 \(u\) 使得在 dfs 序下 \([dfn_u, dfn_u + siz_u - 1]\) 的权值和严格大于 \([1, n]\) 的权值和。
考虑怎么去找,我们先要猜个结论,设 \(pre_i\) 表示在 dfs 序下的权值前缀和,再找到一个 \(j\) 使得 \(pre_{j - 1}, pre_n - pre_j \leq \left \lfloor pre_n \right \rfloor\),则我们找到的点 \(u\) 一定满足 \(j \in [dfn_u, dfn_u + siz_u - 1]\) ,为什么呢?很简单,我们的 \([dfn_u, dfn_u + siz_u - 1]\) 权值和大于总权值一半,而无论是 \(pre_{j - 1}\) 还是 \(pre_n - pre_j\) 即 \(j\) 的左右两边都小于总权值一半,所以我们的区间一定不会被包含于 \([1, j - 1]\) 或 \([j + 1, n]\) ,故 \(rnk_j\) (dfs 序为 \(j\) 的结点) 一定在 \(u\) 的子树中。
\(rnk_j\) 很好找,然后我们因为要找深度最深的那个满足条件的点,且子树和满足单调性,所以满足条件的点一定是在树上连续的,故考虑倍增,每次check跳上去的点是否不满足要求,若是,则跳,反之不跳,最后再跳 \(1\) 步(如果 \(rnk_j\) 满足条件就不管)得到答案 \(u\)。
Solution
挺好实现的,思路清晰,大多都是模板,就是代码微长(也就百来行)。
/*
address:https://vjudge.net/problem/Gym-102759I
AC 2025/10/28 20:29
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int dfn[N], siz[N], top[N], son[N], fa[N], dep[N], rnk[N];
int cntn;
vector<int>G[N];
int n, q;
struct SegmentTree {
#define ls (id << 1)
#define rs (id << 1 | 1)
#define mid (l + r >> 1)
    LL sum[N << 2], add[N << 2];
    inline void pushup(int id) { sum[id] = sum[ls] + sum[rs]; }
    inline void pushdown(int id, int l, int r) {
        if (add[id]) {
            add[ls] += add[id];
            add[rs] += add[id];
            sum[ls] += add[id] * (mid - l + 1);
            sum[rs] += add[id] * (r - mid);
            add[id] = 0;
        }
    }
    inline void modify(int id, int l, int r, int L, int R) {
        if (l >= L && R >= r) {
            sum[id] += r - l + 1;
            ++add[id];
            return;
        }
        pushdown(id, l, r);
        if (L <= mid) modify(ls, l, mid, L, R);
        if (R > mid) modify(rs, mid + 1, r, L, R);
        pushup(id);
    }
    inline LL query(int id, int l, int r, int L, int R) {
        if (l >= L && R >= r) return sum[id];
        pushdown(id, l, r);
        LL ret = 0;
        if (L <= mid) ret += query(ls, l, mid, L, R);
        if (R > mid) ret += query(rs, mid + 1, r, L, R);
        return ret;
    }
    inline int search(int id, int l, int r, LL k) {
        if (l == r) return sum[id] <= k ? l : l - 1;
        pushdown(id, l, r);
        if (sum[ls] <= k) return search(rs, mid + 1, r, k - sum[ls]);
        else return search(ls, l, mid, k);
    }
}SGT;
const int K = 20;
int up[K][N];
inline void dfs1(int u) {
    dep[u] = dep[fa[u]] + 1;
    up[0][u] = fa[u];
    for (int i = 1;i < K;++i) up[i][u] = up[i - 1][up[i - 1][u]];
    siz[u] = 1;son[u] = 0;
    for (auto v : G[u])
        if (v != fa[u]) {
            fa[v] = u;
            dfs1(v);
            siz[u] += siz[v];
            if (siz[v] > siz[son[u]]) son[u] = v;
        }
}
inline void dfs2(int u) {
    dfn[u] = ++cntn;
    rnk[cntn] = u;
    if (!son[u]) return;
    top[son[u]] = top[u];
    dfs2(son[u]);
    for (auto v : G[u])
        if (v != son[u] && v != fa[u]) {
            top[v] = v;
            dfs2(v);
        }
}
LL sum;
inline void update(int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        SGT.modify(1, 1, n, dfn[top[u]], dfn[u]);
        sum += dfn[u] - dfn[top[u]] + 1;
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v);
    SGT.modify(1, 1, n, dfn[u], dfn[v]);
    sum += dfn[v] - dfn[u] + 1;
}
int main() {
    scanf("%d", &n);
    for (int i = 1;i < n;++i) {
        int u, v;scanf("%d%d", &u, &v);
        G[u].push_back(v);G[v].push_back(u);
    }
    dfs1(1);dfs2(1);
    scanf("%d", &q);
    while (q--) {
        int op, u;scanf("%d%d", &op, &u);
        if (op == 1) SGT.modify(1, 1, n, dfn[u], dfn[u] + siz[u] - 1), sum += siz[u];
        if (op == 2) {
            int v;scanf("%d", &v);
            update(u, v);
        }
        int p = SGT.search(1, 1, n, sum >> 1) + 1;
        u = rnk[p];
        for (int i = K - 1;i >= 0;--i)
            if (up[i][u] && SGT.query(1, 1, n, dfn[up[i][u]], dfn[up[i][u]] + siz[up[i][u]] - 1) <= sum >> 1) u = up[i][u];
        if (up[0][u] && SGT.query(1, 1, n, dfn[u], dfn[u] + siz[u] - 1) <= sum >> 1) u = up[0][u];
        printf("%d\n", u);
    }
    return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号