题意
给出若干的点,满足每个点都在所有点形成的凸包上,且点((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
", ans[i]);
}
return 0;
}