[jzoj3865/JSOI2014]士兵部署

来源:互联网 发布:在淘宝网上购物的步骤 编辑:程序博客网 时间:2024/05/22 12:57

题目大意

给定平面上n个整点。m次询问,每次给出一个整点P,问n个点加上P之后形成的凸包面积为多少。

n,m≤100000

分析

首先可以给n个点求个凸包,然后就是计算加上P之后凸包增加的面积。
先判掉凸包是一个点或线段的情况。接下来讲一般情况。
如果能找到过点P的两个切线就可以求增加的面积了。
假设P在凸包外面,那么可以考虑先随便在凸包上确定一个点Q,然后直线PQ和凸包有两个交点(如果这条直线恰好是一条切线则只有一个交点)。这样可以把凸包分成两部分,每一部分以P为原点相邻之间求叉积正好是一段正数一段负数,可以二分求切线。
现在问题是如何求另一个交点。容易发现以P为原点时,凸包上除Q外的点也满足一段在PQ左边,一段在PQ右边。所以也可以二分找交点。
确定了两条切线后,可以用叉积求增加的面积。把式子拆开就是一个区间和的形式了。
时间复杂度O(nlogn)

#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>using namespace std;const int N=1e5+5;typedef long long LL;typedef double db;int n,m,Id,Top;LL ans,sum[N],Out;struct P{    int x,y;    P () {}    P (int _x,int _y)    {        x=_x; y=_y;    }}A[N],B[N],D[N],Q;LL operator * (P a,P b){    return (LL)a.x*b.y-(LL)a.y*b.x;}bool operator < (P a,P b){    return a*b<0;}bool operator <= (P a,P b){    return a*b<=0;}char c;int read(){    int x=0,sig=1;    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;    return x*sig;}int main(){    n=read(); m=read();    for (int i=1;i<=n;i++)    {        int x=read(),y=read(); A[i]=P(x,y);    }    Id=1;    for (int i=2;i<=n;i++) if (A[i].x<A[Id].x || A[i].x==A[Id].x && A[i].y<A[Id].y) Id=i;    for (int i=1,j=0;i<=n;i++) if (i!=Id)    {        B[++j]=A[i]; B[j].x-=A[Id].x; B[j].y-=A[Id].y;    }    sort(B+1,B+n);    D[Top=1]=P(0,0);    for (int i=n-1;i;i--)    {        for (;Top>1 && P(D[Top].x-D[Top-1].x,D[Top].y-D[Top-1].y)<=P(B[i].x-D[Top-1].x,B[i].y-D[Top-1].y);Top--);        D[++Top]=B[i];    }    n=Top; D[n+1]=D[1];    for (int i=1;i<=n+1;i++) D[i].x+=A[Id].x,D[i].y+=A[Id].y;    for (int i=3;i<=n;i++) ans+=P(D[i-1].x-D[1].x,D[i-1].y-D[1].y)*P(D[i].x-D[1].x,D[i].y-D[1].y);    for (int i=2;i<=n+1;i++) sum[i]=sum[i-1]+D[i]*D[i-1];    for (int i,j,x,y,l,r,mid;m--;)    {        x=read(); y=read();        Q=P(D[1].x-x,D[1].y-y);        if (n==1)        {            printf("0.0\n"); continue;        }        if (n==2)        {            printf("%.1lf\n",abs(Q*P(D[2].x-x,D[2].y-y))/2.0); continue;        }        if (P(D[2].x-x,D[2].y-y)<Q)        {            for (l=2,r=n,mid=l+r>>1;l<r;mid=l+r>>1)                if (P(D[mid].x-x,D[mid].y-y)<=Q) l=mid+1;else r=mid;            j=l;            for (l=1,r=j,mid=j>>1;l<r;mid=l+r>>1)                if (P(D[mid].x-x,D[mid].y-y)<P(D[mid+1].x-x,D[mid+1].y-y)) r=mid;else l=mid+1;            i=l+1;            for (l=j,r=n+1,mid=l+r>>1;l<r;mid=l+r>>1)                if (P(D[mid].x-x,D[mid].y-y)<P(D[mid+1].x-x,D[mid+1].y-y)) l=mid+1;else r=mid;            j=l;            Out=ans+(LL)x*(D[j].y-D[i-1].y)+(LL)y*(D[i-1].x-D[j].x)+sum[j]-sum[i-1];        }else        {            for (l=2,r=n,mid=l+r>>1;l<r;mid=l+r>>1)                if (Q<=P(D[mid].x-x,D[mid].y-y)) l=mid+1;else r=mid;            j=l;            for (l=1,r=j,mid=j>>1;l<r;mid=l+r>>1)                if (P(D[mid].x-x,D[mid].y-y)<P(D[mid+1].x-x,D[mid+1].y-y)) l=mid+1;else r=mid;            i=l;            for (l=j,r=n+1,mid=l+r>>1;l<r;mid=l+r>>1)                if (P(D[mid].x-x,D[mid].y-y)<P(D[mid+1].x-x,D[mid+1].y-y)) r=mid;else l=mid+1;            j=l+1;            Out=ans+(LL)x*(D[i].y-D[j-1].y)+(LL)y*(D[j-1].x-D[i].x)+sum[i]+sum[n+1]-sum[j-1];        }        printf("%.1lf\n",Out/2.0);    }    return 0;}
原创粉丝点击