【题解】「JOISC 2021 Day2」道路建设
提供一个二分 + set 的做法。
首先二分一个距离 m i d mid mid ,考虑距离 ≤ m i d \leq mid ≤mid 的点对个数,此时合法点对的个数 ≤ K \leq K ≤K 。得到答案后,再将所有距离 ≤ m i d − 1 \leq mid-1 ≤mid−1 的方案都构造出来,最后输出若干个 m i d mid mid 占位。
我们来考虑直接做这道题,类似于天使玩偶,可以分两个点的位置进行讨论,内部用树状数组求答案,是一个三维偏序的经典问题 (天使玩偶),时间复杂度 O ( n l o g 3 n ) O(nlog^3n) O(nlog3n) 。
这里有一个有用的剪枝,就是当找到 K K K 个点对后,可以直接返回 f a l s e false false ,但是 c d q cdq cdq 的复杂度仍然是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) 。
下面提供一个 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) 的做法,即不会找超过 K K K 个点对,内部用一个带 l o g log log 的 s e t set set 维护。
事实上这道题 可以转化为二维偏序。这是一个非常重要的思想,因为求没有重复元素的三维点对时也可以用二维偏序来计算。
考虑定义 a i = x i + y i a_i=x_i+y_i ai=xi+yi, b i = x i − y i b_i=x_i-y_i bi=xi−yi,点对的新距离为 m a x ( a b s ( a i − a j ) , a b s ( b i − b j ) ) max(abs(a_i-a_j),abs(b_i-b_j)) max(abs(ai−aj),abs(bi−bj)) 。这其实是一个等价转换。然而不同的是,我们去掉了 x i < x j x_i<x_j xi<xj 的限制。
那么我们只需要按照 a i a_i ai 从小到大排序,后面的减去前面的就可以消去绝对值,而后面那个可以直接在 ( b i − K , b i + K ) (b_i-K,b_i+K) (bi−K,bi+K) 的范围内查找,即在 s e t set set 里进行二分查找。
这个方法时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n) ,巧妙运用了绝对值和 m a x max max 函数的性质。
#include <bits/stdc++.h>
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
#define pb push_back
#define fi first
#define se second
#define int long long
using namespace std;
const int mx = 1e6 + 5;
inline int read() {
int X = 0;
bool flag = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
flag = 0;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
X = (X << 1) + (X << 3) + ch - '0';
ch = getchar();
}
if (flag)
return X;
return ~(X - 1);
}
//二分+三维偏序+set
//时间复杂度 O(nlog^3n)
//考虑当合法状态较少时,cdq的复杂度很大
//考虑扔掉一定不产生贡献的答案(实际不可行)
//考虑二维偏序的做法,启发极大
//时间复杂度 O(nlog^2n+Klog^2n) ,其中 n,K 复杂度是分开算的
int n, K, M, cnt, ans[mx];
pii q[mx], X[mx], Y[mx];
set<pii> s;
set<pii>::iterator it;
int dist(int x, int y) {
return max(abs(q[x].fi - q[y].fi), abs(q[x].se - q[y].se));
}
bool check() {
cnt = 0;
s.clear();
int j = 1;
for (int i = 1; i <= n; i++) {
while (j < i && q[i].fi - q[j].fi > M) {
s.erase(mp(q[j].se, j));
j++;
}
it = s.lower_bound(mp(q[i].se - M, 0));
for (; it != s.end(); it++) {
pii now = (*it);
if (now.fi - q[i].se > M) {
break;
}
ans[++cnt] = dist(i, now.se);
if (cnt >= K)
return 0;
}
s.insert(mp(q[i].se, i));
}
return cnt < K;
}
signed main() {
// freopen("data.in","r",stdin);
// freopen("b.in","r",stdin);
// freopen("ans.out","w",stdout);
n = read(), K = read();
for (int i = 1; i <= n; i++) {
int x = read(), y = read();
q[i] = mp(x - y, x + y);
}
sort(q + 1, q + 1 + n);
// M=3; check();
// M=1; if(check()) printf("AC");
// printf("%lld\n",cnt);
int l = 0, r = 4e9, res = 0;
while (l <= r) {
M = (l + r) >> 1;
if (check())
l = M + 1, res = M;
else
r = M - 1;
}
//
// // printf("%lld\n",res);
M = res, check();
sort(ans + 1, ans + 1 + cnt);
for (int i = 1; i <= cnt; i++) {
printf("%lld\n", ans[i]);
}
for (int i = 1; i <= K - cnt; i++) {
printf("%lld\n", M + 1);
}
// printf("%lld",res);
// for(int i=3;i<=7;i++) {
// M=i;
// if(check()) printf("%d\n",i);
// }
}

浙公网安备 33010602011771号