洛谷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
#include
#include
#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.idb.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\).代码在这