P4169 [Violet]天使玩偶/SJY摆棋子 kdtree

(8 mins to read)

要求维护二维平面,支持插点,询问点集中两点的最小曼哈顿距离

做法

kdtree维护不断循环按照其中一维构建二叉树每个点维护子树中的最大最小坐标(最大矩形),然后大力剪枝本题有插入操作,所以可能会退化为链,需要定期重构

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
#include <bits/stdc++.h>
using namespace std;
const int INF = 2e6;
const int N = 6e5 + 5;
const int MAXSIZE = 1 << 20;
char buf[MAXSIZE], *p1, *p2;
inline char gc()
{
return (p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXSIZE,stdin),p1==p2) ? EOF : *p1++);
}
template <class T>
inline void read(T& t)
{
t = 0; int f = 1;
char c = gc();
while(c>'9'||c<'0') {if(c=='-') f = -f; c = gc();}
while(c>='0'&&c<='9') t = t*10 + c - 48, c = gc();
t *= f;
}
int n, m, rt, ans;
constexpr int k = 2;
constexpr double alpha = 0.75;
int D, pool[N], top, tot;
struct point
{
int x[k];
bool operator < (const point &oth) const {
return x[D] < oth.x[D];
}
}a[N], cur;
struct node
{
int mx[k], mn[k];
int l, r, sz;
point tp;
}t[N];
int newnode()
{
if(top) return pool[top--];
return ++tot;
}
void pushup(int p)
{
int l = t[p].l, r = t[p].r;
for(int i=0; i<k; i++)
{
t[p].mn[i] = t[p].mx[i] = t[p].tp.x[i];
if(l) t[p].mn[i] = min(t[p].mn[i], t[l].mn[i]), t[p].mx[i] = max(t[p].mx[i], t[l].mx[i]);
if(r) t[p].mn[i] = min(t[p].mn[i], t[r].mn[i]), t[p].mx[i] = max(t[p].mx[i], t[r].mx[i]);
}
t[p].sz = t[l].sz + t[r].sz + 1;
}
int build(int l, int r, int d)
{
if(l>r) return 0;
int p = newnode(), mid = (l+r)>>1;
D = d;
nth_element(a+l, a+mid, a+r+1);
t[p].tp = a[mid];
t[p].l = build(l, mid-1, (d+1)%k);
t[p].r = build(mid+1, r, (d+1)%k);
pushup(p);
return p;
}
void rebuild(int p, int num)
{
if(t[p].l) rebuild(t[p].l, num);
a[num+t[t[p].l].sz+1] = t[p].tp, pool[++top] = p;
if(t[p].r) rebuild(t[p].r, num+t[t[p].l].sz+1);
}
void test(int &p, int d)
{
if(alpha*t[p].sz<t[t[p].l].sz||alpha*t[p].sz<t[t[p].r].sz)
rebuild(p, 0), p = build(1, t[p].sz, d);
}
void insert(int &p, int d)
{
if(!p)
{
p = newnode();
t[p].tp = cur;
t[p].l = t[p].r = 0;
pushup(p);
return;
}
if(cur.x[d]>t[p].tp.x[d]) insert(t[p].r, (d+1)%k);
else insert(t[p].l, (d+1)%k);
pushup(p); test(p, d);
}
int getdis(int p)
{
int ans = 0;
for(int i=0; i<k; i++)
{
if(cur.x[i]<t[p].mn[i]) ans += (t[p].mn[i]-cur.x[i]);
if(cur.x[i]>t[p].mx[i]) ans += (cur.x[i]-t[p].mx[i]);
}
return ans;
}
int dis(point a, point b)
{
int ans = 0;
for(int i=0; i<k; i++)
ans += abs(a.x[i]-b.x[i]);
return ans;
}
void ask(int p)
{
ans = min(ans, dis(t[p].tp, cur));
int dl = INF, dr = INF;
if(t[p].l) dl = getdis(t[p].l);
if(t[p].r) dr = getdis(t[p].r);
if(dl<dr)
{
if(dl<ans) ask(t[p].l);
if(dr<ans) ask(t[p].r);
}
else
{
if(dr<ans) ask(t[p].r);
if(dl<ans) ask(t[p].l);
}
}
int main()
{
read(n); read(m);
for(int i=1; i<=n; i++) read(a[i].x[0]), read(a[i].x[1]);
rt = build(1, n, 0);
for(int i=1; i<=m; i++)
{
int op;
read(op); read(cur.x[0]); read(cur.x[1]);
if(op==1) insert(rt, 0);
else
{
ans = INF;
ask(rt);
printf("%d\n", ans);
}
}
return 0;
}