发现正着操作几乎是不可做的。所以考虑退回到最后一次操作之后的状态,从后面倒着往前操作。这样删点就变成了加点。
首先把所有的点按照
x
x
坐标排序,坐标相同则只保留
y
y
坐标最大者,然后求出剩下点集的上凸壳。
然后以坐标为关键字,用Splay set维护上凸壳。
考虑加入一个点(下面设为
P
P
)的影响:
(1)如果一个点在凸包内,那么这个点对凸包不造成影响。判断是否在凸包内的方法:在
set
s
e
t
中找到
P
P
的前驱和后继
Y
Y
,然后如果点在
XY
X
Y
之下,那么这个点在凸包内,具体地:
点
P
P
在凸包内
(2)如果一个点在凸包外,这个点会加入凸包,并且可能会弹掉凸包上连续的几个点。
在
P
P
的左边有点,如果
Y
Y
是的前驱,那么如果
则 X X 会被点弹掉。
同样,如果 X X 在的右边, Y Y 是的后继,那么如果
则 X X 会被点弹掉。
具体地,从 P P 开始向左右两边查找会被弹掉的点。
(3)同时,还要维护凸包的周长。
由于一个点最多被点 P P <script type="math/tex" id="MathJax-Element-4600">P</script>弹掉一次,所以复杂度是线性对数级别的。
代码:
#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 2e5 + 2018;
int tn = 3, tm, n, m, q, op[N], val[N], top, tot;
double ans[N], dis;
bool del[N];
struct cyx
{
int x, y;
cyx() {}
cyx(int _x, int _y) :
x(_x), y(_y) {}
friend inline bool operator < (cyx a, cyx b)
{
return a.x < b.x;
}
friend inline cyx operator - (cyx a, cyx b)
{
return cyx(b.x - a.x, b.y - a.y);
}
friend inline int operator * (cyx a, cyx b)
{
return a.x * b.y - a.y * b.x;
}
} a[N], b[N], c[N], stk[N];
set<cyx> s;
typedef set<cyx>::iterator it;
double dist(cyx a, cyx b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
bool comp(cyx a, cyx b)
{
if (a.x != b.x) return a.x < b.x;
return a.y > b.y;
}
void add_point(cyx p)
{
it r = s.lower_bound(p), l = r, tmp; l--;
if ((p - (*l)) * (p - (*r)) < 0) return;
s.insert(p);
dis -= dist(*l, *r);
while (l != s.begin())
{
tmp = l; l--;
if ((p - (*tmp)) * (p - (*l)) > 0) break;
dis -= dist(*tmp, *l);
s.erase(tmp);
}
while (r != s.end())
{
tmp = r; r++;
if ((p - (*r)) * (p - (*tmp)) > 0) break;
dis -= dist(*tmp, *r);
s.erase(tmp);
}
s.insert(p); l = r = s.find(p); l--; r++;
dis += dist(*l, p) + dist(*r, p);
}
int main()
{
int i;
b[1].x = b[1].y = b[2].y = 0; b[2].x = read();
b[3].x = read(); b[3].y = read();
m = read();
For (i, 1, m) b[++tn].x = read(), b[tn].y = read();
q = read();
For (i, 1, q)
{
op[i] = read();
if (op[i] == 1) del[val[i] = read() + 3] = 1;
}
For (i, 1, tn) if (!del[i])
a[++n] = b[i];
sort(a + 1, a + n + 1, comp);
For (i, 1, n) if (i == 1 || a[i].x != a[i - 1].x)
c[++tm] = a[i];
stk[1] = c[1]; stk[top = 2] = c[2];
For (i, 3, tm)
{
while (top > 1 &&
(stk[top - 1] - stk[top]) * (stk[top - 1] - c[i]) >= 0)
top--;
stk[++top] = c[i];
}
For (i, 1, top)
{
s.insert(stk[i]);
if (i < top) dis += dist(stk[i], stk[i + 1]);
}
Rof (i, q, 1)
if (op[i] == 1) add_point(b[val[i]]);
else ans[++tot] = dis;
Rof(i, tot, 1)
printf("%.2lf\n", ans[i]);
return 0;
}