题解:P2632 Explorer

Firsry AC/WA/RE/TLE

人类智慧

蒟蒻太弱了,准高二了还不会用直线解析式算垂足,所以就用玄学连边 A 了。

注意到主要矛盾是优化建边,边的数量又是 Kruskal 的命根子,所以我们只连可能比较有用的边。

观察到两条直线 上面的点在 上的垂足是有单调性的,所以充分发扬人类智慧,设置大小为 的观察窗口以及大小为 的连边窗口,用于找到最优点,并且在最优点附近(可能有用的点)进行连边。

细节处理

  • 为了保证从一端看向另一端,我们需要 sort 一下 t 数组;
  • Kruskal 一定是连成一棵树之后 edgeCount = n + m - 1break
  • 时间空间都很极限,我们的匹配点 match 从数组优化为两个滚动变量 pre, match
  • 观察需要比较大,但是连边不是;(本蒟蒻大量的 MLE 来自这一点没有考虑到 qwq)
  • double 保证高精,不要管为什么 #define dwb double,因为是彩蛋;

upd:AddRange = 5, SeeRange = 50 既可通过此题,更多的极限数据测试已经没脸再交了 qwq。

代码如下:

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
#include<bits/stdc++.h>
#define dwb double

using namespace std;

const int AddRange = 15;
const int SeeRange = 108;

const int MAXN = 100055;
const dwb INF = 1e18;

struct Edge {
int from, to;
dwb weight;
bool operator < (const Edge another) const {
return weight < another.weight;
}
};

int n, m;
int px[5], py[5];
dwb t1[MAXN], t2[MAXN];
dwb x1_, y1_, x2_, y2_;

int anc[MAXN << 1];

vector<Edge> edge;

inline void AncInit(int siz) {
for (int i = 1; i <= siz; ++i)
anc[i] = i;
return;
}
inline int findAnc(int cur) {
if (cur != anc[cur])
anc[cur] = findAnc(anc[cur]);
return anc[cur];
}
inline void merge(int a, int b) {
anc[findAnc(a)] = findAnc(b);
}

inline dwb calX(dwb t, int p1, int p2) {
return px[p1] * t + px[p2] * (1 - t);
}
inline dwb calY(dwb t, int p1, int p2) {
return py[p1] * t + py[p2] * (1 - t);
}
inline dwb calDis() {
return sqrt((x1_ - x2_) * (x1_ - x2_) + (y1_ - y2_) * (y1_ - y2_));
}

inline void kruskal() {
dwb mst = 0;
sort(edge.begin(), edge.end());
AncInit(n + m);
int edgeCount = 0, aim = n + m - 1;
for (Edge e : edge) {
int ancFrom = findAnc(e.from);
int ancTo = findAnc(e.to);
if (ancFrom != ancTo) {
merge(ancFrom, ancTo);
mst += e.weight;
edgeCount++;
if (edgeCount == aim)
break;
}
}
printf("%.3lf", mst);
}

inline void init() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= 4; ++i)
scanf("%d%d", &px[i], &py[i]);
for (int i = 1; i <= n; ++i)
scanf("%lf", &t1[i]);
for (int i = 1; i <= m; ++i)
scanf("%lf", &t2[i]);

sort(t1 + 1, t1 + 1 + n);
sort(t2 + 1, t2 + 1 + m);

for (int i = 2; i <= n; ++i) {
x1_ = calX(t1[i - 1], 1, 2);
y1_ = calY(t1[i - 1], 1, 2);
x2_ = calX(t1[i], 1, 2);
y2_ = calY(t1[i], 1, 2);
edge.push_back({i - 1, i, calDis()});
}
for (int i = 2; i <= m; ++i) {
x1_ = calX(t2[i - 1], 3, 4);
y1_ = calY(t2[i - 1], 3, 4);
x2_ = calX(t2[i], 3, 4);
y2_ = calY(t2[i], 3, 4);
edge.push_back({i - 1 + n, i + n, calDis()});
}
}

inline void build() {
int pre = 1, st = 1, ed = m;
for (int i = 1; i <= n; ++i) {
x1_ = calX(t1[i], 1, 2);
y1_ = calY(t1[i], 1, 2);
if (i != 1) {
st = max(1, pre - SeeRange);
ed = min(m, pre + SeeRange);
}
dwb minDis = INF;
int match = st;
for (int j = st; j <= ed; ++j) {
x2_ = calX(t2[j], 3, 4);
y2_ = calY(t2[j], 3, 4);
dwb dis = calDis();
if (dis < minDis)
minDis = dis, match = j;
}
pre = match;
int add_st = max(1, match - AddRange);
int add_ed = min(m, match + AddRange);
for (int j = add_st; j <= add_ed; ++j) {
x2_ = calX(t2[j], 3, 4);
y2_ = calY(t2[j], 3, 4);
edge.push_back({i, j + n, calDis()});
}
}
}

int main() {
init();
build();
kruskal();
return 0;
}
  • Title: 题解:P2632 Explorer
  • Author: Firsry
  • Created at : 2025-08-10 08:10:51
  • Updated at : 2025-08-10 09:28:29
  • Link: https://firsryfan.github.io/2025/08/10/题解:P2632-Explorer/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
题解:P2632 Explorer