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

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

首先这题显然可以 CDQ,分四个方向分别跑就行了,拆出来的条件是一个三维偏序问题。

但是 $3 \times 10^5$ 的数据范围导致 CDQ 很容易被卡常啊。。(实际上也确实有很多被卡了)。

所以考虑可以用玄学 K-DTree 来维护。

具体来说,考虑从根开始向下行走,每走一个点就更新一下答案。每次查一下这个点到两边矩形的最小距离是否超过了当前的答案,如果是就不走了。

一个优化是,每次先进入距离最小的子树。

这样最坏是 $O(n^2)$ 的,但是随机数据跑得跟 $O(n\log n)$ 一样快。

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
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 600006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
#define min( a , b ) ( (a) < (b) ? (a) : (b) )
#define max( a , b ) ( (a) > (b) ? (a) : (b) )
typedef long long ll;

void rd( int& x ) {
x = 0; char ch = ' ';
while( ch > '9' || ch < '0' ) ch = getchar( );
while( ch >= '0' && ch <= '9' ) x = 10 * x + ch - '0' , ch = getchar();
}

int n , m , rt = 1;

#define ratio 0.75
#define cont( x1 , y1 , x2 , y2 , X1 , Y1 , X2 , Y2 ) ( x1 <= X1 && y1 <= Y1 && x2 >= X2 && y2 >= Y2 )
#define out( x1 , y1 , x2 , y2 , X1 , Y1 , X2 , Y2 ) ( x1 > X2 || y1 > Y2 || x2 < X1 || y2 < Y1 )
#define abs( a ) ( (a) > 0 ? (a) : ( -(a) ) )

struct node {
int d[2];
void in( ) {
rd( d[0] ) , rd( d[1] );
}
} T[MAXN] , P[MAXN] , tmp ;

int cnt , rub[MAXN] , top;
int newnode( ) {
if( top ) return rub[top --];
return ++ cnt;
}

int D;
inline bool cmp( node a , node b ) {
return a.d[D] < b.d[D];
}

int ch[MAXN][2] , lo[MAXN][2] , up[MAXN][2] , siz[MAXN];
inline void pu( int rt ) {
int ls = ch[rt][0] , rs = ch[rt][1];
rep( d , 0 , 1 ) {
lo[rt][d] = up[rt][d] = T[rt].d[d];
if( ls ) lo[rt][d] = min( lo[rt][d] , lo[ls][d] ) , up[rt][d] = max( up[rt][d] , up[ls][d] );
if( rs ) lo[rt][d] = min( lo[rt][d] , lo[rs][d] ) , up[rt][d] = max( up[rt][d] , up[rs][d] );
}
siz[rt] = siz[ls] + 1 + siz[rs];
}
int build( int l , int r , int d ) {
if( l > r ) return 0;
int mid = l + r >> 1 , p = newnode( );
D = d , nth_element( P + l , P + mid , P + r + 1 , cmp ) , T[p] = P[mid];
ch[p][0] = build( l , mid - 1 , d ^ 1 ) , ch[p][1] = build( mid + 1 , r , d ^ 1 );
pu( p );
return p;
}

void pia( int u , int num ) {
if( !u ) return;
pia( ch[u][0] , num );
P[num + siz[ch[u][0]] + 1] = T[u] , rub[++ top] = u;
pia( ch[u][1] , num + siz[ch[u][0]] + 1 );
}

void mt( int& u , int d ) {
if( siz[u] * ratio < siz[ch[u][0]] || siz[u] * ratio < siz[ch[u][1]] )
pia( u , 0 ) , u = build( 1 , siz[u] , d );
}

void ins( int& u , node& x , int d ) {
// cout << u << endl;
if( !u ) { u = newnode( ) , ch[u][0] = ch[u][1] = 0 , T[u] = x , pu( u ); return; }
if( x.d[d] <= T[u].d[d] ) ins( ch[u][0] , x , d ^ 1 );
else ins( ch[u][1] , x , d ^ 1 );
pu( u ); mt( u , d );
}

int dis( int x1 , int y1 , int x2 , int y2 ) {
return abs( x2 - x1 ) + abs( y2 - y1 );
}
int calc( int u , node& t ) { // dis from t to matrix in node u
if( cont( lo[u][0] , lo[u][1] , up[u][0] , up[u][1] , t.d[0] , t.d[1] , t.d[0] , t.d[1] ) ) return 0;
rep( d , 0 , 1 )
if( t.d[d] >= lo[u][d] && t.d[d] <= up[u][d] ) return min( abs( t.d[d ^ 1] - lo[u][d ^ 1] ) , abs( t.d[d ^ 1] - up[u][d ^ 1] ) );
int x = t.d[0] , y = t.d[1];
return min( min( dis( x , y , lo[u][0] , lo[u][1] ) , dis( x , y , lo[u][0] , up[u][1] ) ) , min( dis( x , y , up[u][0] , up[u][1] ) , dis( x , y , up[u][0] , lo[u][1] ) ) );
}

int ans;
void que( int u , node& x ) {
// cout << u << endl;
if( !u ) return;
ans = min( ans , dis( x.d[0] , x.d[1] , T[u].d[0] , T[u].d[1] ) );
int ls = 0x3f3f3f3f , rs = 0x3f3f3f3f;
if( ch[u][0] ) ls = min( ls , calc( ch[u][0] , x ) );
if( ch[u][1] ) rs = min( rs , calc( ch[u][1] , x ) );
if( ls < ans && ls <= rs ) {
que( ch[u][0] , x );
if( rs < ans ) que( ch[u][1] , x );
} else if( rs < ans && rs <= ls ) {
que( ch[u][1] , x );
if( ls < ans ) que( ch[u][0] , x );
}
}

void solve() {
cin >> n >> m;
rep( i , 1 , n ) {
P[i].in();
}
build( 1 , n , 0 );
int op;
rep( i , 1 , m ) {
rd( op ); tmp.in( );
if( op == 1 ) {
ins( rt , tmp , 0 );
} else {
ans = 0x3f3f3f3f;
que( rt , tmp );
printf("%d\n",ans);
}
}
}

signed main() {
// freopen("P4169_2.in","r",stdin);
// freopen("fuckout","w",stdout);
// int T;cin >> T;while( T-- ) solve();
solve();
}
文章作者: yijan
文章链接: https://yijan.co/p4169-violettian-shi-wan-ou-sjy-bai-qi-zi/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog