Minkowski和

两个凸集的和(每两个不同集的x,y分别相加得新点)为凸集。

将两个凸集按同样时针求向量,极角排序后按向量走既得新凸集(但还需对应点平移)。

应用:求两凸集距离。

area

求新凸集面积。

不需平移,直接叉积。

这道题证明二分快排是可以卡的。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>

struct point
{
       long long x,y,c;
};

const double eps=1e-6;
point a[300000],b[3][300000],p[300000];
int n,m,tot,t[3],st[3][300000];

int cmper(const void *i,const void *j)
{
    point *p=(point *)i,*q=(point *)j;
    if (0==(p->y - q->y)) return (p->x-q->x);
//    if (p->y == q->y) return p->x - q->x;
    return p->y-q->y;
}

inline long long cross(point e,point r)
{
       return ((e.x*r.y)-(e.y*r.x));
}

int check(int c,int i,int j,int k)
{
    point u,v,w;
    u=a[i],v=a[j],w=a[k]; 
    point e,r;
    e.x=v.x-u.x,e.y=v.y-u.y;
    r.x=w.x-u.x,r.y=w.y-u.y;
    long long kk=cross(e,r);
    return (kk>=0) ? 1 : 0;
}

void work(int c,int s)
{
     qsort(a+1,s,sizeof(a[1]),cmper);
     memset(st[c],0,sizeof(st[c]));
     t[c]=1;st[c][t[c]]=1;
     int i,k;
     for (i=2;i<=s;i++)
     {
         for (;(t[c]>1)&&(check(c,st[c][t[c]-1],st[c][t[c]],i));t[c]--) ;
         st[c][++t[c]]=i;
     }
     k=t[c];
     for (i=s-1;i>0;i--)
     {
         for (;(t[c]>k)&&(check(c,st[c][t[c]-1],st[c][t[c]],i));t[c]--) ;
         st[c][++t[c]]=i;         
     }
     if (s>1) t[c]--;
 }

void init()
{
     scanf("%d%d\n",&n,&m);
     int i;
     for (i=1;i<=n;i++)
     {
         scanf("%I64d%I64d",&a[i].x,&a[i].y);
//         b[1][i]=a[i];
     }
     scanf("\n");
     work(1,n);
     for (i=1;i<=n;i++) b[1][i]=a[i];
     memset(a,0,sizeof(a));
     for (i=1;i<=m;i++)
     {
         scanf("%I64d%I64d",&a[i].x,&a[i].y);
//         b[2][i]=a[i];         
     }
     scanf("\n");
     work(2,m);
     for (i=1;i<=m;i++) b[2][i]=a[i];     
 }
 
long long equ(long long x)
{
    if ((x>-eps)&&(x<eps)) return 1;
    return 0;
}
 
void doit(int c)
{
     int i;
     for (i=2;i<=t[c];i++)
     {
         tot++;
         p[tot].x=b[c][st[c][i]].x-b[c][st[c][i-1]].x;
         p[tot].y=b[c][st[c][i]].y-b[c][st[c][i-1]].y;
      if ((p[tot].x>0)&&((p[tot].y>0)||(equ(p[tot].y)))) p[tot].c=1;
      if (((p[tot].x<0)||(equ(p[tot].x)))&&(p[tot].y>0)) p[tot].c=2;      
      if ((p[tot].x<0)&&((p[tot].y<0)||(equ(p[tot].y)))) p[tot].c=3;      
      if (((p[tot].x>0)||(equ(p[tot].x)))&&(p[tot].y<0)) p[tot].c=4;      
     }
     tot++;
      p[tot].x=b[c][st[c][1]].x-b[c][st[c][i-1]].x;
      p[tot].y=b[c][st[c][1]].y-b[c][st[c][i-1]].y;
      if ((p[tot].x>0)&&((p[tot].y>0)||(equ(p[tot].y)))) p[tot].c=1;
      if (((p[tot].x<0)||(equ(p[tot].x)))&&(p[tot].y>0)) p[tot].c=2;      
      if ((p[tot].x<0)&&((p[tot].y<0)||(equ(p[tot].y)))) p[tot].c=3;      
      if (((p[tot].x>0)||(equ(p[tot].x)))&&(p[tot].y<0)) p[tot].c=4;      
 }

inline int chek(point a,point b)
{
    if (a.c<b.c) return 1 ;
    else if (a.c>b.c) return 0;
    long long k=cross(a,b);
//    if (k<=0) return 0;
    if (k>0) return 1;
    return 0;
}

void qs(int l,int r)
{
     int i=l,j=r;
     point x=p[rand () % (r - l + 1) + l],c;
//     long long xx=cross(x,x);     
     for (;i<=j;)
     {
         for (;chek(p[i],x);i++) ;
         for (;chek(x,p[j]);j--) ;
         if (i<=j) 
         {
                     c=p[i],p[i]=p[j],p[j]=c;
                     i++,j--;
         }
     }
     if (i<r) qs(i,r);
     if (l<j) qs(l,j);
 }

int cmp(const void *i,const void *j)
{
    point a=*(point*)i,b=*(point*)j;
    if (a.c<b.c) return -1 ;
    else if (a.c>b.c) return 1;
    long long k=cross(a,b);
//    if (k<=0) return 0;
    if (k>0) return -1;
    return 1;
}

void make()
{
//     qs(1,tot);
     qsort(p+1,tot,sizeof(p[1]),cmp)  ;
     int i;
     long long ans=0;
     point u,v;
     u.x=0,u.y=0;
     for (i=1;i<=tot;i++)
     {
         v.x=u.x+p[i].x;
         v.y=u.y+p[i].y;
         ans+=cross(u,v);
         u=v;
     }
     if (ans<eps) ans=-ans;
     printf("%I64d\n",ans);
 }

int main()
{
    freopen("area.in","r",stdin);
    freopen("area.out","w",stdout);
        srand( int(time(NULL)));                           
        init();
        tot=0;
        doit(1);
        doit(2);
        make();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值