CF1899G Unusual Entertainment
CF1899G Unusual Entertainment
Problem
一棵树是一个无环连通图。
排列是一个由 \(n\) 个互不相同的整数 \(1\) 到 \(n\) 以任意顺序组成的数组。例如,\([5, 1, 3, 2, 4]\) 是一个排列,但 \([2, 1, 1]\) 不是排列(因为 \(1\) 在数组中出现了两次),\([1, 3, 2, 5]\) 也不是排列(因为 \(n = 4\),但数组中出现了 \(5\))。
在 BrMeast 视频拍摄失败后,Alex 陷入了抑郁。即使是他的生日也没有让他开心起来。然而,在收到 Timofey 的礼物后,Alex 的心情突然变好了。现在他整天都在玩这份礼物。最近,他想出了一个不同寻常的娱乐方式。
Alex 用他的积木搭建了一棵有 \(n\) 个顶点的树,顶点编号为 \(1\) 到 \(n\),根节点为顶点 \(1\)。然后他将 \(1\) 到 \(n\) 的每个整数按某种顺序写下来,得到一个排列 \(p\)。接着,Alex 想出了 \(q\) 个三元组 \(l, r, x\)。对于每个三元组,他尝试判断在顶点 \(p_l, p_{l+1}, \ldots, p_r\) 中,是否至少存在一个是顶点 \(x\) 的后代。
如果满足 \(\mathrm{dist}(1, v) + \mathrm{dist}(v, u) = \mathrm{dist}(1, u)\),则称顶点 \(u\) 是顶点 \(v\) 的后代,其中 \(\mathrm{dist}(a, b)\) 表示顶点 \(a\) 和 \(b\) 之间的距离。换句话说,顶点 \(v\) 必须在从根到顶点 \(u\) 的路径上。
Alex 把这个娱乐方式告诉了 Zakhar。现在 Alex 会告诉他的朋友 \(q\) 个如上所述的三元组,希望 Zakhar 能帮他判断是否存在后代。Zakhar 很困,所以他向你求助。请帮助 Zakhar 回答所有 Alex 的问题,让他能早点睡觉。
Thinking
显然时可以把点的值替换成 \(P_i\)
然后变成了判一个区间内的值是否是一个点的后继
考虑用一种数据结构维护子树内的点,而这个信息时不会通过树的某些性质压缩的,只能用数据结构暴力维护
想到值域线段树+线段树合并
做完了。。。
不难发现Treap也可做,跟线段树一样
但如果不想写合并可以写启发式合并
这样就可以暴力合并但是会多一只log
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, q, pos[N], rt[N], ls[N * 40], rs[N * 40], cnt[N * 40], tot;
vector<int> e[N];
void upd(int &p, int l, int r, int id) {
if (!p) p = ++tot;
cnt[p]++;
if (l == r) return;
int mid = (l + r) >> 1;
if (id <= mid) upd(ls[p], l, mid, id);
else upd(rs[p], mid + 1, r, id);
}
int merge(int x, int y, int l, int r) {
if (!x || !y) return x | y;
int z = ++tot;
cnt[z] = cnt[x] + cnt[y];
if (l == r) return z;
int mid = (l + r) >> 1;
ls[z] = merge(ls[x], ls[y], l, mid);
rs[z] = merge(rs[x], rs[y], mid + 1, r);
return z;
}
int query(int p, int l, int r, int ql, int qr) {
if (!p || ql > r || qr < l) return 0;
if (ql <= l && r <= qr) return cnt[p];
int mid = (l + r) >> 1;
return query(ls[p], l, mid, ql, qr) + query(rs[p], mid + 1, r, ql, qr);
}
void dfs(int u, int fa) {
upd(rt[u], 1, n, pos[u]);
for (int v : e[u]) if (v != fa) {
dfs(v, u);
rt[u] = merge(rt[u], rt[v], 1, n);
}
}
void solve() {
cin >> n >> q;
for (int i = 1; i <= n; i++) e[i].clear(), rt[i] = 0;
for (int i = 1; i <= tot; i++) ls[i] = rs[i] = cnt[i] = 0;
tot = 0;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
for (int i = 1, x; i <= n; i++) {
cin >> x;
pos[x] = i;
}
dfs(1, 0);
while (q--) {
int l, r, x;
cin >> l >> r >> x;
cout << (query(rt[x], 1, n, l, r) ? "YES" : "NO") << "\n";
}
cout << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t; cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号