Project Euler 816

Project Euler 816

题目

Shortest distance among points

We create an array of points \(P_n\) in a two dimensional plane using the following random number generator:

\(\begin{aligned} &s_0=290797\\ &s_{n+1}={s_n}^2 \bmod 50515093\\ &P_n=(s_{2n},s_{2n+1}) \end{aligned}\)

Let \(d(k)\) be the shortest distance of any two (distinct) points among \(P_0, \cdots, P_{k - 1}\).

E.g. \(d(14)=546446.466846479\)

Find \(d(2000000)\). Give your answer rounded to \(9\) places after the decimal point.

解决方案

经典问题平面最近点对问题。使用分治的思想可以解决。大概的做法是:

  • 分解:将一个平面上的所有点按照\(x\)坐标划分成相等的两部分;
  • 结局:然后递归求解这两部分的解;
  • 合并:假设这条划分接线为\(x=x_0\),那么将\(x=x_0\)附近的点再按照\(y\)轴进行排序,再枚举这些点之间距离的最小值即可。最终得到了这一部分点的最小点对长度。

代码

本做法的时间复杂度为\(O(n\log^2 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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6;
ll INF = 1e17;
struct P {
ll x, y;
bool operator<(const P &p) const {
return x < p.x || x == p.x && y < p.y;
}
ll dis2(P &p) const {
ll dx = x - p.x, dy = y - p.y;
return dx * dx + dy * dy;
}
};
P p[N + 4];
int tp[N + 4];
ll dfs(int l, int r) {
if (l == r)
return INF;
if (l + 1 == r)
return p[l].dis2(p[r]);
int mid = (l + r) >> 1;
ll ans = min(dfs(l, mid - 1), dfs(mid, r));
int k = 0;
for (int i = l; i <= r; i++) {
ll dx = p[mid].x - p[i].x;
if (dx * dx <= ans)
tp[++k] = i;
}
sort(tp + 1, tp + k + 1, [](const int &a,const int &b){return p[a].y < p[b].y; });
for (int i = 1; i <= k; i++) {
for (int j = i + 1; j <= k; j++) {
ll dy = p[tp[j]].y - p[tp[i]].y;
if(dy * dy > ans) break;
ans = min(ans, p[tp[i]].dis2(p[tp[j]]));
}
}
return ans;
}
void input(int N) {
ll s = 290797;
for (int i = 1; i <= N; i++) {
p[i].x = s; s = s * s % 50515093;
p[i].y = s; s = s * s % 50515093;
}
}
int main()
{
input(N);
sort(p + 1, p + N + 1);
double ans = sqrt(dfs(1, N));
printf("%.9f\n", ans);
}