您的位置:首页 > Web前端 > JavaScript

[jzoj3865/JSOI2014]士兵部署

2017-10-19 16:40 260 查看

题目大意

给定平面上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
,Out;

struct P
{
int x,y;
P () {}
P (int _x,int _y)
{
x=_x; y=_y;
}
}A
,B
,D
,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: