看到红蓝两维偏序,马上醒过来了。
然后发现还有一维偏序是子树,这个可以通过拍扁树得到 dfn 序得到。
然后子树转化为区间,可以红蓝两维排序、归并解决掉,而第三维 dfn 通过树状数组维护区间和。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15, M = N << 1;
int n;
int h[N], e[M], ne[M], idx = 0;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
struct node {
int id, dfn, sz, red, blue;
//blue 写成 bule 让我想起了一位故人。
} a[N], b[N];
bool cmp(node a, node b) {
if (a.red != b.red) return a.red < b.red;
if (a.blue != b.blue) return a.blue < b.blue;
return a.dfn > b.dfn;
}
int dfn[N], sz[N], tot = 0;
void dfs(int u, int father) {
sz[u] = 1, dfn[u] = ++tot;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == father) continue;
dfs(v, u);
sz[u] += sz[v];
}
}
int tr[N];
void change(int x, int y) {
for ( ; x < N; x += x & -x) tr[x] += y;
}
int query(int x) {
int res = 0;
for ( ; x ; x -= x & -x) res += tr[x];
return res;
}
int ans[N];
void cdq(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq(l, mid), cdq(mid + 1, r);
for (int i = l, j = mid + 1, k = l; k <= r; k++) {
if ((j > r) || (i <= mid && a[i].blue <= a[j].blue)) change(a[i].dfn, 1), b[k] = a[i++];
else {
ans[a[j].id] += query(a[j].dfn + a[j].sz - 1) - query(a[j].dfn - 1);
b[k] = a[j++];
}
}
for (int i = l; i <= mid; i++) change(a[i].dfn, -1);
for (int i = l; i <= r; i++) a[i] = b[i];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) h[i] = -1;
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
dfs(1, 0);
for (int i = 1, x, y; i <= n; i++) scanf("%d%d", &x, &y), a[i] = (node){i, dfn[i], sz[i], x, y};
sort(a + 1, a + 1 + n, cmp);
cdq(1, n);
for (int i = 1; i <= n; i++)
if (ans[i]) printf("%d\n", ans[i]);
return 0;
}