题意:
给定平面上的 n 个点,定义两点距离为曼哈顿距离,现要将点分成 k (k < n) 组,定义组内距离 d 为最大的两点距离,求 m i n { m a x { d 1 , d 2 , . . . , d k } } min\{max\{d_1, d_2, ... , d_k\}\} min{max{d1,d2,...,dk}}。(n <= 1e4)
链接:
https://vjudge.net/problem/POJ-3241
解题思路:
题意转化为求曼哈顿距离最小生成树上的第 k 大边,即第 n - k 小边。
显然,我们无法将边都建出来。首先看最小生成树的一个性质:
对于图中的一个环,删去环上的最大边,不影响最小生成树。
故对于平面图上的任意三点连接成的三角形,可舍去其中最大的边。
更近一步,如图:(距离为默认为曼哈顿距离)
对每个点 A,划分出 8 个 45 度区域,图示为其中一个区域。对于该区域的任意点 B、C,有
m
a
x
{
A
B
,
A
C
}
>
B
C
max\{AB, AC\} > BC
max{AB,AC}>BC,证明略。因此,A 只与该区域距离 A 最近的点连边(图示中的红色边,图例中的黄色边会在枚举B、C时筛选)。由于边双向,故只需考虑四个区域,边数最多为 4n。
接下来考虑如何得到最近的点。对于点
A
(
x
0
,
y
0
)
A(x_0,y_0)
A(x0,y0),在图示区域的点
B
(
x
,
y
)
B(x, y)
B(x,y) 满足
y
>
=
y
0
y >= y_0
y>=y0,
y
−
x
<
=
y
0
−
x
0
y - x <= y_0 - x_0
y−x<=y0−x0,二维偏序问题,用树状数组维护即可。
参考代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e4 + 5;
const int maxm = 1e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
struct Edge{
int u, v, w;
bool operator < (const Edge &o) const{
return w < o.w;
}
} ei[maxm];
struct Node{
int x, y, p;
bool operator < (const Node &o) const{
return y == o.y ? x > o.x : y > o.y;
}
} a[maxn], b[maxn];
int xi[maxn], tot;
int c[maxn], pi[maxn];
int pre[maxn];
int n, m, k;
#define lowb(x) ((x)&(-x))
void update(int x, int v, int p){
while(x <= tot){
if(v < c[x]) c[x] = v, pi[x] = p;
x += lowb(x);
}
}
int query(int x){
int p = pi[x], ret = c[x];
while(x){
if(c[x] < ret) ret = c[x], p = pi[x];
x -= lowb(x);
}
return ret != inf ? p : 0;
}
int fin(int x){
return x == pre[x] ? x : pre[x] = fin(pre[x]);
}
int kruskal(){
for(int i = 1; i <= n; ++i) pre[i] = i;
sort(ei + 1, ei + 1 + m);
int cnt = 0, lim = n - k, ret;
for(int i = 1; i <= m && cnt < lim; ++i){
int u = ei[i].u, v = ei[i].v, w = ei[i].w;
u = fin(u), v = fin(v);
if(u == v) continue;
pre[u] = v, ++cnt, ret = w;
}
return ret;
}
int dis(int i, int j){
return abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y);
}
void build(){
m = 0;
for(int i = 1; i <= n; ++i) b[i] = a[i];
for(int d = 0; d < 4; ++d){
if(d == 1 || d == 3){
for(int i = 1; i <= n; ++i) swap(b[i].x, b[i].y);
}
else if(d == 2){
for(int i = 1; i <= n; ++i) b[i].x = -b[i].x;
}
tot = 0;
for(int i = 1; i <= n; ++i) xi[++tot] = b[i].y - b[i].x;
sort(b + 1, b + 1 + n);
sort(xi + 1, xi + 1 + tot);
tot = unique(xi + 1, xi + 1 + tot) - xi - 1;
for(int i = 1; i <= tot; ++i) c[i] = inf;
for(int i = 1; i <= n; ++i){
int x = lower_bound(xi + 1, xi + 1 + tot, b[i].y - b[i].x) - xi;
int p = query(x);
if(p) ei[++m] = {b[i].p, p, dis(b[i].p, p)};
update(x, b[i].y + b[i].x, b[i].p);
}
}
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
while(scanf("%d%d", &n, &k) != EOF){
for(int i = 1; i <= n; ++i) scanf("%d%d", &a[i].x, &a[i].y), a[i].p = i;
build();
int ret = kruskal();
printf("%d\n", ret);
}
return 0;
}