二维数点一般基于二维前缀和+扫描线思想,用树状数组维护。

例题

Genealogy in the trees

注:这题有更好的做法,因为自己第一时间想到这种方法,所以用这种方法写了,顺便练习下树剖和二维数点

题目要求“有多少个 i 满足: pi 可以到达 a 且 b 可以到达 qi。”

也就是在{p,q}中,求第一维范围是$[1到a的路径]$,第二维范围是$[b的子树]$的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Problem: Genealogy in the trees
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/64819/D
// Memory Limit: 2097152 MB
// Time Limit: 4000 ms

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
using ll = long long;

bool multi = 0;

int n, m, Q;
const int N = 3e5 + 10;
typedef pair<int, int> PII;
vector<vector<int>> edge;
int id[N],cnt;
int dep[N],sz[N],top[N],fa[N],son[N];
pair<int, int> w[N], q[N];

struct Node {
int x, y, sgn, id;
bool operator<(const Node &w) const{
return x < w.x;
}
};

vector<Node> op;
vector<PII> v;

int tr[N];
inline int lowbit(int x){
return x&-x;
}
void add(int x,int c){
for(int i=x;i<N;i+=lowbit(i)){
tr[i]+=c;
}
}
int presum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)){
res+=tr[i];
}
return res;
}

void dfs1(int u,int father,int depth){
dep[u]=depth,fa[u]=father,sz[u]=1;
for(auto v:edge[u]){
if(v==father) continue;
dfs1(v,u,depth+1);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
}

void dfs2(int u,int t){
id[u]=++cnt,top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(auto v:edge[u]){
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}

void insert_op(int x1, int y1, int x2, int y2, int id) {
op.push_back((Node){x2, y2, 1, id});
op.push_back((Node){x1 - 1, y1 - 1, 1, id});
op.push_back((Node){x1 - 1, y2, -1, id});
op.push_back((Node){x2, y1 - 1, -1, id});
}

void get_path(int u,int v,PII y,int i){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
insert(id[top[u]], y.first, id[u], y.second, i);
u=fa[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
insert(id[v], y.first, id[u], y.second, i);
}

int ans[N];

void count_point() {
sort(v.begin(), v.end());
sort(op.begin(), op.end());
int cur = 0;
for(auto [x, y, sgn, id]: op) {
while(cur < (int)v.size() && v[cur].first <= x) add(v[cur++].second, 1);
ans[id] += sgn * presum(y);
// cout << x << ' ' << y << ' ' << sgn * presum(y) << '\n';
}
}

void solve() {
cin >> n >> m >> Q;
edge.resize(n + 1);
for(int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
edge[u].push_back(v);
}

// exit(0);
dfs1(1, -1, 1);
dfs2(1, 1);

// for(int i = 1; i <= n; i++) {
// cout << sz[i] << " \n"[i == n];
// }

for(int i = 1; i <= m; i++) {
cin >> w[i].first >> w[i].second;
w[i].first = id[w[i].first], w[i].second = id[w[i].second];
v.push_back({w[i].first, w[i].second});
}
for(int i = 1; i <= Q; i++) {
cin >> q[i].first >> q[i].second;
PII y = {id[q[i].second], id[q[i].second] + sz[q[i].second] - 1};
get_path(1, q[i].first, y, i);
}

// for(auto it: v) {
// cout << it.first << ' ' << it.second << '\n';
// }
// cout << '\n';N

count_point();

for(int i = 1; i <= Q; i++) {
cout << ans[i] << '\n';
}
}

signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
if (multi) cin >> T;
while (T--) {
solve();
}

return 0;
}