洛谷P4631/uoj415/loj2586 [APIO2018] Circle selection 选圆圈
考虑和一个圆有交集的圆,是平面上的一块区域,所以我们考虑用 \(KDtree\) 维护。
两个圆相交的条件 \(\displaystyle (X_i-X_j)^2+(Y_i-Y_j)^2 \leqslant (R_i+R_j)^2\)
考虑怎么剪枝,直接维护 \(X,Y,R\) 的话不好维护。我们知道圆 \(i\) 和圆 \(j\) 有交集的话肯定会和框住圆 \(j\) 的最小的正方形有交集。
所以我们维护 \(KDtree\) 一个子树里所有圆对应的正方形 \(X,Y\) 的最值来剪枝即可。
\(yuan\) 里的 \(l,r\) :对应的正方形的左下角和右上角的坐标。
\(shu\) 里的 \(l,r\) :子树里正方形 \(x,y\) 坐标的最小值和最大值。
#include<algorithm>
#include<iostream>
#include<cstdio>
#define int long long
#define lson tr[k].ls
#define rson tr[k].rs
using namespace std;
int n, nowk, cnt, root;
const int N = 300010;
int s[N], ans[N];
struct dian {int x, y;};
struct yuan {int x, y, R, id; dian l, r;} cir[N];
struct shu {int ls, rs, id; dian l, r;} tr[N];
int my1(yuan a, yuan b) {return a.R == b.R ? a.id<b.id: a.R>b.R;}
int my2(int a, int b) {return nowk ? cir[a].x < cir[b].x : cir[a].y < cir[b].y;}
void pushup(int k)
{
tr[k].l = cir[tr[k].id].l; tr[k].r = cir[tr[k].id].r;
if (lson)tr[k].l.x = min(tr[k].l.x, tr[lson].l.x), tr[k].l.y = min(tr[k].l.y, tr[lson].l.y), tr[k].r.x = max(tr[k].r.x, tr[lson].r.x), tr[k].r.y = max(tr[k].r.y, tr[lson].r.y);
if (rson)tr[k].l.x = min(tr[k].l.x, tr[rson].l.x), tr[k].l.y = min(tr[k].l.y, tr[rson].l.y), tr[k].r.x = max(tr[k].r.x, tr[rson].r.x), tr[k].r.y = max(tr[k].r.y, tr[rson].r.y);
}
void build(int &k, int l, int r, int now)
{
if (l > r)return;
int mid = (l + r) >> 1;
nowk = now; nth_element(s + l, s + mid, s + r + 1, my2);
k = ++cnt; tr[k].id = s[mid];
build(lson, l, mid - 1, now ^ 1); build(rson, mid + 1, r, now ^ 1);
pushup(k);
}
int p2(int x) {return x * x;}
int jiao(int i, int j) {return p2(cir[i].x - cir[j].x) + p2(cir[i].y - cir[j].y) <= p2(cir[i].R + cir[j].R);}
void change(int k, int id)
{
if (!k)return;
if (cir[id].r.x < tr[k].l.x || tr[k].r.x < cir[id].l.x || cir[id].r.y < tr[k].l.y || tr[k].r.y < cir[id].l.y)return;
if (!ans[cir[tr[k].id].id] && jiao(tr[k].id, id))ans[cir[tr[k].id].id] = cir[id].id;
if (lson)change(lson, id); if (rson)change(rson, id);
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; ++i)
{
scanf("%lld%lld%lld", &cir[i].x, &cir[i].y, &cir[i].R);
cir[i].l.x = cir[i].x - cir[i].R; cir[i].l.y = cir[i].y - cir[i].R; cir[i].r.x = cir[i].x + cir[i].R; cir[i].r.y = cir[i].y + cir[i].R;
cir[i].id = i; s[i] = i;
}
sort(cir + 1, cir + 1 + n, my1); build(root, 1, n, 0);
for (int i = 1; i <= n; ++i)
if (!ans[cir[i].id])change(root, i);
for (int i = 1; i <= n; ++i)printf("%lld ", ans[i]);
return 0;
}
然而 \(uoj\) 和 \(loj\) 上有 \(hack\) 数据,我们只要充分发挥人类智慧旋转一下坐标系就行了。记得开 \(long \space double\).代码在这