HDU7150. Static Query on Tree (2022杭电多校第2场1001)
HDU7150. Static Query on Tree (2022杭电多校第2场1001)
题意
给定一棵树,\(n\) 个结点。根为 \(1\),所有的结点只能走向其父亲结点。
有 \(q\) 次询问,每次询问给出 \(3\) 个结点集合 \(A,B,C\)。问树上有多少点满足如下条件:
- 该点可以从集合 \(A\) 中的至少一个结点到达。
- 该点可以从集合 \(B\) 中的至少一个结点到达。
- 该点可以到达集合 \(C\) 中的至少一个结点。
分析
对于集合 \(A\) 中的一个点,它能到达的结点是包括他自己向上一直到根的路径。
对于集合 \(B\) 中的一个点,它能到达的结点是包括他自己向上一直到根的路径。
对于集合 \(C\) 中的一个点,它以及它的后代都能到达这个点。
实际上,问题转化为有多少个点同时满足上面三个性质。可以开一个线段树,对于集合 \(A\) 的对应点打上 \(a\) 标记,集合 \(B\) 的对应点打上 \(b\) 标记。最后统计对于 \(C\) 中的每一个点为根的子树,统计完一个点,消去以这个点为根的子树上的所有 \(a,b\) 标记。这可以使用树链剖分+线段树维护,注意每次询问都要清空所有标记,可以使用一个清空 tag
来保证复杂度。
代码
#include <iostream>
#include <vector>
#define now nodes[rt]
#define ls nodes[rt << 1]
#define rs nodes[rt << 1 | 1]
using namespace std;
const int maxn = 2e5 + 10;
int n, q;
vector<int> G[maxn];
int fa[maxn], son[maxn], dep[maxn], sz[maxn], dfn[maxn], rk[maxn], top[maxn];
int tot;
void init() {
tot = 0;
}
void dfs(int u) {
sz[u] = 1;
dep[u] = dep[fa[u]] + 1;
for (auto v : G[u]) {
dfs(v);
if (sz[v] > sz[son[u]])
son[u] = v;
sz[u] += sz[v];
}
}
void dfs(int u, int t) {
top[u] = t;
dfn[u] = ++tot;
rk[tot] = u;
if (!son[u])
return;
dfs(son[u], t);
for (auto v : G[u]) {
if (v == son[u])
continue;
dfs(v, v);
}
}
struct SegTree {
struct Node {
int l, r;
int a, b, ab;
int tag_a, tag_b, tag_cls;
} nodes[maxn << 2];
void build(int rt, int l, int r) {
now = {l, r, 0, 0, 0, 0, 0, 0};
if (l == r)
return;
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void pushup(int rt) {
now.a = ls.a + rs.a;
now.b = ls.b + rs.b;
now.ab = ls.ab + rs.ab;
}
void update_node_a(int rt) {
now.tag_a = 1;
now.a = now.r - now.l + 1;
now.ab = now.b;
}
void update_node_b(int rt) {
now.tag_b = 1;
now.b = now.r - now.l + 1;
now.ab = now.a;
}
void update_node_cls(int rt) {
now.tag_cls = 1;
now.a = now.b = now.ab = now.tag_a = now.tag_b = 0;
}
void pushdown(int rt) {
if (now.tag_cls) {
update_node_cls(rt << 1);
update_node_cls(rt << 1 | 1);
now.tag_cls = 0;
}
if (now.tag_a) {
update_node_a(rt << 1);
update_node_a(rt << 1 | 1);
now.tag_a = 0;
}
if (now.tag_b) {
update_node_b(rt << 1);
update_node_b(rt << 1 | 1);
now.tag_b = 0;
}
}
void update(int rt, int L, int R, void (SegTree::*op)(int rt)) {
if (L <= now.l && now.r <= R) {
(this->*op)(rt);
return;
}
pushdown(rt);
if (L <= ls.r)
update(rt << 1, L, R, op);
if (R >= rs.l)
update(rt << 1 | 1, L, R, op);
pushup(rt);
}
int query(int rt, int L, int R) {
if (L <= now.l && now.r <= R) {
return now.ab;
}
pushdown(rt);
int ans = 0;
if (L <= ls.r)
ans += query(rt << 1, L, R);
if (R >= rs.l)
ans += query(rt << 1 | 1, L, R);
return ans;
}
} seg;
void update_chain(int u, void (SegTree::*op)(int rt)) {
int v = 1;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]])
swap(u, v);
seg.update(1, dfn[top[u]], dfn[u], op);
u = fa[top[u]];
}
if (dep[u] > dep[v])
swap(u, v);
seg.update(1, dfn[u], dfn[v], op);
}
void update_tree(int u, void (SegTree::*op)(int rt)) {
seg.update(1, dfn[u], dfn[u] + sz[u] - 1, op);
}
int query_tree(int u) {
return seg.query(1, dfn[u], dfn[u] + sz[u] - 1);
}
void solve() {
cin >> n >> q;
for (int i = 2; i <= n; i++) {
cin >> fa[i];
G[fa[i]].push_back(i);
}
dfs(1);
dfs(1, 1);
seg.build(1, 1, n);
while (q--) {
int a, b, c, t;
int ans = 0;
cin >> a >> b >> c;
for (int i = 0; i < a; i++) {
cin >> t;
update_chain(t, &SegTree::update_node_a);
}
for (int i = 0; i < b; i++) {
cin >> t;
update_chain(t, &SegTree::update_node_b);
}
for (int i = 0; i < c; i++) {
cin >> t;
ans += query_tree(t);
update_tree(t, &SegTree::update_node_cls);
}
cout << ans << '\n';
update_tree(1, &SegTree::update_node_cls);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}