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,依次插入。
- 插入“管辖点”\(2\):\(2\) 的管辖范围就是以 \(1\) 为根的子树。
- 插入“管辖点”\(6\):\(6\) 处于 \(2\) 的管辖范围中,故处理与 \(2\) 的冲突。二者的中点 \(1\) 应该属于 \(2\) 管辖,故 \(6\) 的管辖范围为以 \(6\) 为根的子树。
- 插入“管辖点”\(3\):\(3\) 处于 \(2\) 的管辖范围中,故处理与 \(2\) 的冲突。二者的中点 \(3\) 应该属于 \(3\) 管辖,故 \(3\) 的管辖范围为以 \(3\) 为根的子树。
- 插入“管辖点”\(7\):\(7\) 处于 \(3\) 的管辖范围中,故处理与 \(3\) 的冲突。二者的中点 \(7\) 应该属于 \(7\) 管辖,故 \(7\) 的管辖范围为以 \(7\) 为根的子树。
- 插入“管辖点”\(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;
}

浙公网安备 33010602011771号