JSOI2018 战争(凸包+闵可夫斯基和)

JSOI2018 战争

题解

  • 每次询问暴力判断时间无法满足,考虑是否可以求出使两凸包有交或无交的向量所在的范围。
  • 若两个凸包平移向量 w w w后有交,等价于 ∃ a ∈ A , b ∈ B , a = b + w exists ain A,bin B,a=b+w aA,bB,a=b+w,即 ∃ a ∈ A , b ∈ B , w = a − b exists ain A,bin B, w=a-b aA,bB,w=ab,则所有 w w w的取值范围是 { a − b ∣ a ∈ A , b ∈ B } {a-b|ain A,bin B} {abaA,bB},把 b b b向量全部取反,和 a a a做一遍闵可夫斯基和得到的凸包就是能使两凸包有交的向量范围(包括凸包的边)。
  • 最后只需要判断向量是否在凸包上,如果用回转数或光线投射复杂度是 O ( n q ) O(nq) O(nq)的,可以先把凸包最左下角的点平移到 ( 1 , 1 ) (1,1) (1,1),二分出该向量的极角序在哪两个顶点之间,然后判断该向量是否在这两个向量和 ( 1 , 1 ) (1,1) (1,1)构成的三角形内,复杂度降为 O ( q log ⁡ 2 n ) O(qlog_2 n) O(qlog2n)

代码

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 200010
#define ld long double
#define ll long long
struct no {
	ll x, y, s;
	ld d;
}a[N], b[N], a0[N], b0[N], q[N];
int n, m, t;
ll sqr(ll x) {
	return x * x;
}
int cmp(no x, no y) {
	if(x.y == y.y) return x.x < y.x;
	return x.y < y.y;
}
int cmp1(no x, no y) {
	if(x.d == y.d) return x.s > y.s;
	return x.d < y.d;
}
no in(no x, no y) {
	return {x.x + y.x, x.y + y.y};
}
no de(no x, no y) {
	return {x.x - y.x, x.y - y.y};
}
ll ct(no x, no y) {
	return x.x * y.y - x.y * y.x;
}
void solve() {
	int i;
	sort(a + 1, a + n + 1, cmp);
	for(i = 2; i <= n; i++) {
		a[i].d = atan2(a[i].y - a[1].y, a[i].x - a[1].x);
		a[i].s = sqr(a[i].y - a[1].y) + sqr(a[i].x - a[1].x);
	}
	sort(a + 2, a + n + 1, cmp1);
	t = 1;
	q[1] = a[1];
	for(i = 2; i <= n; i++) if(i == 2 || a[i].d != a[i - 1].d) {
		while(t > 1 && ct(de(q[t], q[t - 1]), de(a[i], q[t])) < 0) t--;
		q[++t] = a[i];
	}
}
int main() {
	int Q, i;
	scanf("%d%d%d", &n, &m, &Q);
	for(i = 1; i <= n; i++) {
		scanf("%lld%lld", &a[i].x, &a[i].y);
	}
	for(i = 1; i <= m; i++) {
		scanf("%lld%lld", &b[i].x, &b[i].y);
		b[i].x = -b[i].x, b[i].y = -b[i].y;
	}
	solve();
	int at = t;
	for(i = 1; i <= t; i++) a0[i] = q[i];
	n = m;
	for(i = 1; i <= m; i++) a[i] = b[i];
	solve();
	int bt = t; 
	for(i = 1; i <= t; i++) b0[i] = q[i];
	int tot = 0;
	for(i = 1; i <= at; i++) {
		a[++tot] = de(a0[i % at + 1], a0[i]);
		a[tot].d = atan2(a[tot].y, a[tot].x);
		if(a[tot].d < 0) a[tot].d += M_PI * 2.0;
	}
	for(i = 1; i <= bt; i++) {
		a[++tot] = de(b0[i % bt + 1], b0[i]);
		a[tot].d = atan2(a[tot].y, a[tot].x);
		if(a[tot].d < 0) a[tot].d += M_PI * 2.0;
	}
	sort(a + 1, a + tot + 1, cmp1);
	q[1] = in(a0[1], b0[1]);
	for(i = 1; i < tot; i++) q[i + 1] = in(q[i], a[i]);
	for(i = 2; i <= tot; i++) {
		q[i].d = atan2(q[i].y - q[1].y, q[i].x - q[1].x);
	}
	while(Q--) {
		no x;
		scanf("%lld%lld", &x.x, &x.y);
		x.d = atan2(x.y - q[1].y, x.x - q[1].x);
		if(x.d > q[tot].d || x.d < 0) {
			printf("0
");
			continue;
		}
		int l = 1, r = tot - 1, s;
		while(l <= r) {
			int mid = (l + r) / 2;
			if(x.d >= q[mid].d) s = mid, l = mid + 1; else r = mid - 1;
		}
		if(ct(de(x, q[s]), de(q[s + 1], x)) <= 0) printf("1
"); else printf("0
");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/LZA119/p/14279505.html