动态凸包
时间限制: 5 Sec 内存限制: 256 MB
题目描述
给出一个点集pset,按顺序将点pi(1<=i<=10^5)加入前i-1个点所形成的凸包中,如果点pi落在了凸包外,更新凸包
对于每一个点pi输出当前形成凸包的面积。初始时,点集pset中有3个点。
样例输入
1 0 0 3 3 2
5
1 2
2 1
3 0
2 3
0 1
样例输出
8
8
12
14
16
题解
本来只想到维护水平序凸包,分别维护上下凸壳即可,然而细节比较多。wbs大爷告诉我可以维护极角序凸包,这样只要写一个凸包(orxwbs!!!)。
用一个平衡树/set维护凸包即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<algorithm>
#define ll long long
#define iter multiset<node>::iterator
using namespace std;
int n;ll S;
struct node{
int a,b;double ang;
bool operator<(const node &x)
const{return ang<x.ang||(ang==x.ang&&b<x.b);}
node operator-(const node &x){return (node){a-x.a,b-x.b,ang};}
ll operator*(const node &x){return (ll)a*x.b-(ll)b*x.a;}
}t[4],o,p;
multiset<node>q;
double get(node x){return (double)atan2(x.b,x.a);}
iter getpre(iter x){if(x==q.begin())x=q.end();return (--x);}
iter getnxt(iter x){return (++x)==q.end()?q.begin():x;}
node operator-(iter x,iter y){return (node)*x-(node)*y;}
ll operator*(iter x,iter y){return ((node)*x)*((node)*y);}
int main()
{
for(int i=1;i<=3;i++)
{
scanf("%d%d",&t[i].a,&t[i].b);
o.a+=t[i].a;o.b+=t[i].b;
t[i].a*=3;t[i].b*=3;
}
for(int i=1;i<=3;i++)
t[i].ang=(double)get(t[i]-o),q.insert(t[i]-o);
sort(t+1,t+4);
S=t[1]*t[2]+t[2]*t[3]+t[3]*t[1];
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&p.a,&p.b);
p.a*=3;p.b*=3;p=p-o;p.ang=get(p);
iter nxt=q.lower_bound(p);
if(nxt==q.end())nxt=q.begin();
iter pre=getpre(nxt);
if((nxt-pre)*(p-*pre)<0)
{
S-=pre*nxt;
iter pos=getpre(pre);
while((pre-pos)*(p-*pos)<=0)
{
S-=pos*pre;q.erase(pre);
pre=pos;pos=getpre(pos);
}
pos=getnxt(nxt);
while((nxt-pos)*(p-*pos)>=0)
{
S-=nxt*pos;q.erase(nxt);
nxt=pos;pos=getnxt(pos);
}
S+=p*(*nxt)-(p*(*pre));q.insert(p);
}
printf("%lld\n",S/9);
}
return 0;
}