P9062 Ynoi2002 区间平面最近点对
Usada_Pekora · · 题解
2.10
看题。
容易发现这个题是在询问一个区间内的点集里的平面最近点对。
2.11
15 分:暴力,
平面最近点对有一些很优秀的算法,随便选一些出来用就好了。
因为是 Ynoi,而且时限很宽松,所以不考虑分治这种 polylog 的结构,发现有一些依赖于排序,且正确性很高的随机化算法可以用,即按照某种策略排序后取排好的序列里面相近的 chkmin,所以不好删除,回滚一下就好了,
换一个比较厉害的平衡树,比如 Trie(好像有人叫它多叉线段树?),于是有一个
用一些奇技淫巧,又通过了 case 9,15,16,17,有 60 分。此时似乎把所有还没通过的人的解缝合起来,就可以解决所有测试点了。
今天就摆到这里吧,剩下的明天想。
2.12
考虑平面最近点对的一个线性做法,即划分方格。常规全局做法是:random_shuffle 整个点集,然后将整个平面划分成若干个 hash_map 维护每个方格的点集,其中
以下内容经大佬指点。
在不考虑复杂度的前提下,维护固定边长
怎么优化呢?只要先维护小方格,然后维护大方格的时候可以去掉小方格里包含的情况,即:小方格里相邻的贡献已经统计过了,此时再维护不会更优。
考虑维护长度
枚举次数
查询的话,因为每次都是在前缀上插入一个点,可以像扫描线一样去做。每个点维护的信息是它后面的点与它构成的最小距离,查询区间最小值即可,由于我们有
实现的话,不算很卡常,不手写任何数据结构可以过,如果你感觉过不去,考虑手写哈希即可。下面代码保留了一部分卡常的内容,这样才知道你做的是 Ynoi,注意洛谷的评测姬波动已经达到了一个点 1s,所以你就算手写哈希也有可能过不了。
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define mp MP[s]
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
const int N = 2.5e5 + 5, V = 1e8, dx[] = {1, 1, 1, -1, -1, -1, 0, 0, 0}, dy[] = {1, 0, -1, 1, 0, -1, 1, 0, -1};
int _x[N], _y[N], bx[N], by[N], n, Q, bel[N];
ll v1[N], v2[N], ans[N];
inline void chkmin(ll &x, ll y) {
x = min(x, y);
}
inline void chkmax(ll &x, ll y) {
x = max(x, y);
}
inline ll uid(int x, int y) {
return 10ll * x * V + y;
}
inline ll dis(int i, int j) {
return 1ll * (_x[i] - _x[j]) * (_x[i] - _x[j]) + 1ll * (_y[i] - _y[j]) * (_y[i] - _y[j]);
}
inline void modify(int x, ll k) {
chkmin(v1[x], k);
chkmin(v2[bel[x]], k);
}
inline ll query(int l, int r) {
int bl = bel[l], br = bel[r];
ll res = 1e18;
if (bl == br) {
for (int i = l; i <= r; i++)
chkmin(res, v1[i]);
return res;
} else {
for (int i = l; bel[i] == bl; i++)
chkmin(res, v1[i]);
for (int i = bl + 1; i <= br; i++)
chkmin(res, v2[i]);
}
return res;
}
vector<int> point[N * 30];
vector<pair<int, int>> q[N];
gp_hash_table<ll, int> MP[30];
signed main() {
// freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> Q;
for (int i = 1; i <= n; i++)
cin >> _x[i] >> _y[i];
const int sq = sqrt(n);
for (int i = 1; i <= n; i++)
bel[i] = (i - 1) / sq + 1;
for (int i = 1; i <= Q; i++) {
int l, r;
cin >> l >> r;
q[r].emplace_back(make_pair(l, i));
}
for (int i = 1; i <= n; i++)
v1[i] = v2[i] = 1e18;
for (int i = 1; i <= Q; i++)
ans[i] = 1e18;
int idx = 0;
for (int i = 1; i <= n; i++) {
for (int s = 1; (1 << s) <= V; s++) {
int x = _x[i] >> s, y = _y[i] >> s;
if (mp.find(uid(x, y)) == mp.end()) {
mp[uid(x, y)] = ++idx;
point[idx].clear();
}
for (int j = 0; j < 9; j++) {
int nx = x + dx[j], ny = y + dy[j];
if (nx < 0 || ny < 0 || nx > (V >> s) || ny > (V >> s))
continue;
if (mp.find(uid(nx, ny)) != mp.end()) {
int pid = mp[uid(nx, ny)];
vector<int> tmp;
for (int k : point[pid]) {
ll dik = dis(i, k);
modify(k, dik);
if (dik >= (1ll << (s + s - 2)))
tmp.emplace_back(k);
}
point[pid] = tmp;
if (j == 7)
point[pid].emplace_back(i);
}
}
}
for (auto &[l, id] : q[i])
ans[id] = query(l, i);
}
for (int i = 1; i <= Q; i++)
cout << ans[i] << '\n';
return 0;
}