【BZOJ5035】【JSOI2014】士兵部署

【题目链接】

【思路要点】

  • 问题本质上是给定一个凸包,多次询问加入一个点后新凸包的面积。
  • 若一个点在凸包内,那么面积显然不变,否则,我们需要求出该点对凸包的两个切点,一旦求出了切点,问题显然可以通过部分和解决。
  • 以下给出了一种求解切点的思路:
  • 任取凸包内一点,对凸包上的点和询问点极角排序。
  • 对于每个询问点,在与之极角相差在\(\pi\)以内的凸包上的一段点上二分,这一段点一定由一段该点能看到的点接上一段该点不能看到的点组成,极角与该询问点相差最大的能被该点看到的凸包上的点即为切点。其中“能被看到”一条件可以用叉积判断。
  • 实现细节较为繁琐,读者可阅读代码加深理解。
  • 时间复杂度\(O((N+M)LogN)\)。

代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
const double pi = acos(-1);
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct point {long long x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, long long b) {return (point) {a.x * b, a.y * b}; }
point operator / (point a, long long b) {return (point) {a.x / b, a.y / b}; }
long long operator * (point a, point b) {return a.x * b.y - a.y * b.x; }
bool operator < (point a, point b) {
	if (a.y == b.y) return a.x < b.x;
	else return a.y < b.y;
}
long long len(point a) {
	return a.x * a.x + a.y * a.y;
}
double PolarAngle(point a) {
	return atan2(a.y, a.x);
}
struct info {point pos; double alpha; int home; };
int n, q, top; point a[MAXN], sum;
info b[MAXN], c[MAXN];
long long ans[MAXN];
double angle[MAXN];
bool cmp(point x, point y) {
	if ((x - a[1]) * (y - a[1]) == 0) return len(x - a[1]) < len(y - a[1]);
	else return (x - a[1]) * (y - a[1]) > 0;
}
bool cnp(info x, info y) {
	return x.alpha < y.alpha;
}
int main() {
	read(n), read(q);
	for (int i = 1; i <= n; i++)
		read(a[i].x), read(a[i].y);
	for (int i = 2; i <= n; i++)
		if (a[i] < a[1]) swap(a[1], a[i]);
	sort(a + 2, a + n + 1, cmp);
	a[++n] = a[1]; top = 1;
	for (int i = 2; i <= n; i++) {
		while (top >= 2 && (a[i] - a[top]) * (a[i] - a[top - 1]) >= 0) top--;
		a[++top] = a[i];
	}
	n = top - 1;
	long long tans = 0;
	for (int i = 1; i <= n; i++) {
		tans += a[i] * a[i + 1];
		sum = sum + a[i];
	}
	sum = sum / n;
	for (int i = 1; i <= n; i++)
		b[i] = (info) {a[i] - sum, PolarAngle(a[i] - sum), 0};
	sort(b + 1, b + n + 1, cnp);
	for (int i = 1; i <= n; i++) {
		b[i + n] = b[i];
		b[i + n].alpha += 2 * pi;
		b[i + n * 2] = b[i + n];
		b[i + n * 2].alpha += 2 * pi;
	}
	for (int i = 1; i <= q; i++) {
		read(c[i].pos.x), read(c[i].pos.y);
		c[i].pos = c[i].pos - sum;
		c[i].alpha = PolarAngle(c[i].pos) + 2 * pi;
		c[i].home = i;
	}
	sort(c + 1, c + q + 1, cnp);
	static long long pre[MAXN];
	for (int i = 2; i <= 3 * n; i++)
		pre[i] = pre[i - 1] + b[i - 1].pos * b[i].pos;
	int now = 1, pred = 1, succ = 1;
	for (int i = 1; i <= q; i++) {
		while (b[now].alpha < c[i].alpha) now++;
		while (b[now].alpha - b[pred].alpha >= pi) pred++;
		while (b[succ + 1].alpha - b[now - 1].alpha <= pi) succ++;
		if ((b[now].pos - b[now - 1].pos) * (c[i].pos - b[now - 1].pos) >= 0) ans[c[i].home] = tans;
		else {
			int l = pred, r = now - 1;
			while (l < r) {
				int mid = (l + r) / 2;
				if ((c[i].pos - b[mid].pos) * (b[mid + 1].pos - b[mid].pos) >= 0) r = mid;
				else l = mid + 1;
			}
			int tmp = l; l = now, r = succ;
			while (l < r) {
				int mid = (l + r + 1) / 2;
				if ((c[i].pos - b[mid].pos) * (b[mid - 1].pos - b[mid].pos) <= 0) l = mid;
				else r = mid - 1;
			}
			ans[c[i].home] = tans - (pre[l] - pre[tmp]) + b[tmp].pos * c[i].pos + c[i].pos * b[l].pos;
		}
	}
	for (int i = 1; i <= q; i++)
		if (ans[i] % 2 == 0) printf("%lld.0\n", ans[i] / 2);
		else printf("%lld.5\n", ans[i] / 2);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80081172