window.cnblogsConfig = {//可以放多张照片,应该是在每一个博文上面的图片,如果是多张的话,那么就随机换的。 homeTopImg: [ "https://i.loli.net/2019/08/02/5d440029af8e994539.png" ], }

P3233 [HNOI2014] 世界树的题解

P3233 [HNOI2014] 世界树的题解

不是,虚树的题目为什么要用虚树做

【题目大意】

给定一颗无根树,共有 \(n\) 个节点,给定一些询问,每次询问定义 \(m\) 个“管辖点”,树上的节点 \(v\)\(u\) 管辖当且仅当:\(u\) 是“管辖点”;\(u\) 是所有“管辖点”中距离 \(v\) 最近的点;若有多个距离相同的点,\(u\) 是编号最小的。举个例子:在下图中,如果只有 \(4\)\(6\) 是“管辖点”,那么 \(2\) 应该被 \(4\) 管辖。

【具体思路】

众所不周知,这是一道很板的虚树题,如果你学过虚树,你会很套路地想到用虚树来解决这题,在这里提供一种不用虚树的做法。

我们观察一下题目的性质:

  • 每一个“管辖点”管辖的范围是一个包含点 \(m\) 的连通块

我们不妨另 \(1\) 为根,那么“管辖点”\(i\) 所管辖的范围就是以 \(i\) 的某一代父亲为根的子树减去一堆小子树。

举个例子:在样例 2 7 3 6 9 中,\(3\) 所管辖的范围就是以 \(3\) 为根的子树减去以 \(7\) 为根的子树减去以 \(9\) 为根的子树,\(2\) 所管辖的范围就是以 \(1\) 为根的子树减去以 \(3\) 为根的子树减去以 \(6\) 为根的子树。

因此,一个较为清晰的思路就出来了,我们按照每个点的深度为“管辖点”排序,然后依次插入每一个“管辖点”,新插入的“管辖点”只会和一个已插入的“管辖点”的管辖范围产生冲突,按照题目意思处理就行了。

处理方式:我们观察到产生冲突的点一定是一棵子树,那么我们只要能够快速(以 \(\log n\) 的复杂度级别)找到这个根,这里可以先处理出分界点要从新管辖点向上跳多少步,然后使用倍增解决,然后对于它的子树内做一个区间修改,这一部分可以使用线段树来解决。这样,我们就从较为暴力的 \(O(n^2)\) 优化到了 \(O(n\log n)\)

【具体步骤】

还是以样例 2 7 3 6 9 为例,排序后的“管辖点”为 2 6 3 7 9,依次插入。

  1. 插入“管辖点”\(2\)\(2\) 的管辖范围就是以 \(1\) 为根的子树。
  2. 插入“管辖点”\(6\)\(6\) 处于 \(2\) 的管辖范围中,故处理与 \(2\) 的冲突。二者的中点 \(1\) 应该属于 \(2\) 管辖,故 \(6\) 的管辖范围为以 \(6\) 为根的子树。
  3. 插入“管辖点”\(3\)\(3\) 处于 \(2\) 的管辖范围中,故处理与 \(2\) 的冲突。二者的中点 \(3\) 应该属于 \(3\) 管辖,故 \(3\) 的管辖范围为以 \(3\) 为根的子树。
  4. 插入“管辖点”\(7\)\(7\) 处于 \(3\) 的管辖范围中,故处理与 \(3\) 的冲突。二者的中点 \(7\) 应该属于 \(7\) 管辖,故 \(7\) 的管辖范围为以 \(7\) 为根的子树。
  5. 插入“管辖点”\(9\)\(9\) 处于 \(3\) 的管辖范围中,故处理与 \(3\) 的冲突。二者的中点 \(4\) 应该属于 \(3\) 管辖,故 \(9\) 的管辖范围为以 \(9\) 为根的子树。

【温馨提示】

在处理向上跳的距离的时候,要注意满足题目条件,如果有一个点到两个管辖点距离相同,一定要按编号选择。

#include <bits/stdc++.h>
#define fre(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define ref(i, a, b) for (int i = (a); i < (b); i++)
#define reb(i, a, b) for (int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;

const int N = 3e5 + 5;

class Segment_tree
{
private:
    int t[N * 4];
public:
#define ls (k << 1)
#define rs (k << 1 | 1)
    void modify(int k, int l, int r, int L, int R, int x)
    {
        if (r < L || R < l) return;
        if (L <= l && r <= R) return t[k] = x, void();
        if (t[k]) t[ls] = t[rs] = t[k];
        t[k] = 0;
        int mid = (l + r) >> 1;
        modify(ls, l, mid, L, R, x), modify(rs, mid + 1, r, L, R, x);
    }
    int query(int k, int l, int r, int pos)
    {
        if (t[k]) return t[k];
        int mid = (l + r) >> 1;
        if (pos <= mid) return query(ls, l, mid, pos);
        else return query(rs, mid + 1, r, pos);
    }
#undef ls
#undef rs
}tre;

int n;
vector<int>e[N];


int fa[N], dep[N], sz[N];
int tot, dfn[N];

int rt[N], tr[N], table[N];
int fff[N][20];

void dfs(int u, int f)
{
    sz[u] = 1;
    fa[u] = f;
    dep[u] = dep[f] + 1;
    dfn[u] = ++tot;
    for (auto v : e[u]) if (v != f)
        dfs(v, u), sz[u] += sz[v];
}
int LCA(int u, int v)
{
    if (dep[u] > dep[v]) swap(u, v);
    reb(s, 19, 0) if (dep[fff[v][s]] >= dep[u]) v = fff[v][s];
    reb(s, 19, 0) if (fff[u][s] != fff[v][s]) u = fff[u][s], v = fff[v][s];
    return u == v ? u : fa[u];
}

void init()
{
    dfs(1, 0);
    rep(i, 1, n) fff[i][0] = fa[i];
    rep(s, 1, 19) rep(i, 1, n) fff[i][s] = fff[fff[i][s - 1]][s - 1];
}

int m;
struct node{
    int dis, dfn, pos;
    bool operator< (node &x){ return this->dis == x.dis ? this->dfn < x.dfn : this->dis < x.dis; }
}ask[N];
int ans[N], as[N];

void solve()
{
    // work
    cin >> m;
    rep(i, 1, m) cin >> as[i], ask[i] = {dep[as[i]], as[i], i};
    sort(ask + 1, ask + m + 1);

    int u = ask[1].dfn, v;
    tre.modify(1, 1, n, 1, n, 1);
    tr[u] = 1, table[1] = u, ans[u] = n;
    rep(i, 2, m)
    {
        v = ask[i].dfn;
        u = table[tre.query(1, 1, n, dfn[v])];
        int lca = LCA(u, v);
        int len = (dep[u] - dep[lca] + dep[v] - dep[lca] - 1 + (u > v)) >> 1;
        int t = v;
        while (len)
        {
            int xxx = len & (-len);
            len -= xxx;
            t = fff[t][__lg(xxx)];
        }
        tr[v] = t, table[t] = v;
        tre.modify(1, 1, n, dfn[t], dfn[t] + sz[t] - 1, t);
        ans[u] -= sz[t], ans[v] += sz[t];
    }
    rep(i, 1, m) cout << ans[as[i]] << " \n"[i == m], ans[as[i]] = 0, table[tr[as[i]]] = 0, tr[as[i]] = 0;
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 1, u, v; i < n; i++)
    {
        cin >> u >> v;
        e[u].push_back(v), e[v].push_back(u);
    }
    init();
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}
posted @ 2026-07-11 22:35  mgcjade  阅读(20)  评论(0)    收藏  举报