loj6504. 「雅礼集训 2018 Day5」Convex

题意

给出若干的点,满足每个点都在所有点形成的凸包上,且点\((0, 0)\)在凸包内。
若干次询问一段区间构成凸包的面积。

题解

考虑莫队。
但是发现莫队在加入一个点的时候还要在当前凸包上找前驱和后继,这个复杂度有一个\(\log\),难以优化。
但是删除的时候,我们发现,如果我们维护一个凸包上的点的前驱后继的一个链表,是很方便删除的。
所以需要一个只删除不插入的莫队,这相当于一个回滚莫队。
这个回滚莫队只要有删除操作和撤销操作就好了。由于删除操作比较简单,撤销操作便容易实现。
因此可以在\(\mathcal O(n \sqrt n)\)的时间内解决此题。
顺带复习下回滚莫队:先按左端点所在块排,左端点在同一个块内按照右端点排,每次询问完之后左指针回滚到询问左端点所在块的右边界。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1.5e5 + 10, BLO = 350;
int n, m, id[N], pre[N], suf[N]; ll sum, ans[N];
struct P {
	int x, y;
	bool operator < (const P &o) const {
		return atan2(y, x) < atan2(o.y, o.x);
	}
	ll operator * (const P &o) const {
		return (ll)x * o.y - (ll)o.x * y;
	}
} a[N];
struct Q {
	int l, r, id;
	bool operator < (const Q &o) const {
		return (l - 1) / BLO == (o.l - 1) / BLO ? r > o.r : l < o.l;
	}
} q[N];
bool cmp (int x, int y) {
	return a[x] < a[y];
}
void del (int x) {
	int l = pre[x], r = suf[x];
	sum -= a[l] * a[x] + a[x] * a[r] - a[l] * a[r];
	suf[l] = r, pre[r] = l;
}
void udel (int x) {
	int l = pre[x], r = suf[x];
	sum += a[l] * a[x] + a[x] * a[r] - a[l] * a[r];
	suf[l] = pre[r] = x;
}
int main () {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d%d", &a[i].x, &a[i].y), id[i] = i;
	}
	sort(id + 1, id + n + 1, cmp), id[0] = id[n];
	for (int i = 1; i <= n; ++i) {
		pre[id[i]] = id[i - 1], suf[id[i - 1]] = id[i];
		sum += a[id[i - 1]] * a[id[i]];
	}
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
	}
	sort(q + 1, q + m + 1);
	for (int L = 1, R, i = 1, l = 1, r = n; i <= m; L = R + 1) {
		R = min(n, L + BLO - 1);
		for ( ; i <= m && q[i].l <= R; ++i) {
			for ( ; r > q[i].r; del(r--));
			for ( ; l < q[i].l; del(l++));
			ans[q[i].id] = sum;
			for ( ; l > L; udel(--l));
		}
		for ( ; r < n; udel(++r));
		for ( ; l <= R; del(l++));
	}
	for (int i = 1; i <= m; ++i) {
		printf("%lld\n", ans[i]);
	}
	return 0;
}
posted @ 2019-12-19 09:52  psimonw  阅读(288)  评论(0编辑  收藏  举报