动态凸包

动态凸包

时间限制: 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;
}

你可能感兴趣的:(动态凸包)