// 完全搞不懂版权到底是个什么东西..不过如果内容侵犯版权的话请提醒我删除=A=
题目大意:
给你若n个士兵,对于一个点,如果从该点出发,朝任意一个方向移动,都会和至少一个士兵的距离减少,则称这个点是可以被守卫的。可以守卫的区域,就是所有这样的点的集合。
然后有m个询问,每个询问输入一个士兵坐标,问你如果加入该士兵,守卫的区域面积是多大。
对于30%的数据满足n,m≤500;
对于60%的数据满足n,m≤10000,并且至多只有500个位置满足部署新士兵之后,可以守卫的区域的面积会增加;
对于100%的数据满足n,m≤10^5,−10^8≤Xi,Yi,Ui,Vi≤10^8。
因为版权问题担惊受怕啊。。目前只写出了60分的。
很显然。守卫面积就是凸包面积。首先,我们维护出一个凸包。然后对于每个询问,二分求出士兵的位置以及士兵是否在一开始的凸包内部。
如果不在则从二分出的位置开始向两边枚举。用差积算出增加的面积。【这里复杂度是O(n)的】
然后就好了。。60分get√
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
struct points
{
double x,y;
}a[100011],chs[100011];
inline double max(double x,double y)
{
if(x>y)
return x;
return y;
}
inline double xabs(double x)
{
if(x<0)
x=-x;
return x;
}
inline double dis(points x,points y)
{
return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
inline double multi(points p0, points p1, points p2)
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
inline bool cmp(points x,points y)
{
if(multi(x,y,a[1])>0)
return true;
if(multi(x,y,a[1])==0&&dis(x,a[1])<dis(y,a[1]))
return true;
return false;
}
int main()
{
freopen("soldier.in","r",stdin);
freopen("soldier.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y);
double minx=2100000000,miny=2100000000;
int d=0;
for(i=1;i<=n;i++)
{
if(a[i].y<miny)
{
minx=a[i].x;
miny=a[i].y;
d=i;
}
else if(a[i].y==miny)
{
if(a[i].x<minx)
{
minx=a[i].x;
d=i;
}
}
}
points t;
t=a[d];
a[d]=a[1];
a[1]=t;
sort(a+2,a+1+n,cmp);
int sp=1,k=2;
chs[0]=a[n];
chs[1]=a[1];
while(k<=n)
{
double x=multi(chs[sp],chs[sp-1],a[k]);
if(x<=0)
{
sp++;
chs[sp]=a[k];
k++;
}
else
sp--;
}
double ans=0;
for(i=2;i<=sp-1;i++)
ans+=xabs(multi(chs[1],chs[i],chs[i+1]));
//printf("%.1lf\n",ans/double(2));
double x,y;
chs[sp+1]=chs[1];
for(i=1;i<=m;i++)
{
scanf("%lf%lf",&x,&y);
int l=2,r=sp+1,mid;
points po;
po.x=x;
po.y=y;
bool flag=false;
while(l<=r)
{
mid=(l+r)/2;
double tx,ty;
tx=multi(chs[mid+1],chs[1],po);
ty=multi(chs[mid],chs[1],po);
if(tx<=0&&ty<=0)
l=mid+1;
else if(tx>0&&ty<=0)
{
flag=true;
break;
}
else
r=mid-1;
}
if(flag)
{
bool ff=false;
double exans=ans;
if(chs[mid+1].x>chs[mid].x)
{
double k=(chs[mid+1].y-chs[mid].y)/(chs[mid+1].x-chs[mid].x);
double xt=chs[mid].y-chs[mid].x*k;
if(po.y-po.x*k>xt&&a[1].y-a[1].x*k<=xt||po.y-po.x*k<=xt&&a[1].y-a[1].x*k>xt)
{
ff=true;
exans+=xabs(multi(chs[mid+1],chs[mid],po));
}
}
else if(chs[mid+1].x==chs[mid].x)
{
if(po.x>chs[mid].x&&a[1].x<=chs[mid].x||po.x<=chs[mid].x&&a[1].x>chs[mid].x)
{
ff=true;
exans+=xabs(multi(chs[mid+1],chs[mid],po));
}
}
else
{
double k=(chs[mid].y-chs[mid+1].y)/(chs[mid].x-chs[mid+1].x);
double xt=chs[mid].y-chs[mid].x*k;
if(po.y-po.x*k>xt)
{
ff=true;
exans+=xabs(multi(chs[mid+1],chs[mid],po));
}
}
if(ff)
{
int ll=mid,rr=mid+1;
while(multi(chs[ll],chs[ll-1],po)>0&&ll>1)
{
exans+=xabs(multi(chs[ll],chs[ll-1],po));
ll--;
}
while(multi(chs[rr],chs[rr+1],po)<0&&rr<sp)
{
exans+=xabs(multi(chs[rr+1],chs[rr],po));
rr++;
}
}
printf("%.1lf\n",exans/double(2));
}
else
{
double exans=ans;
int ll=sp,rr=2;
if(multi(chs[2],chs[1],po)>0)
{
exans+=xabs(multi(chs[2],chs[1],po));
//while(multi(chs[rr],chs[1],po)<multi(chs[rr+1],chs[1],chs[rr])&&rr<sp)
while(multi(chs[rr],chs[rr+1],po)<0&&rr<sp)
{
exans+=xabs(multi(chs[rr+1],chs[rr],po));
rr++;
}
}
if(multi(chs[sp],chs[1],po)<0)
{
exans+=xabs(multi(chs[sp],chs[1],po));
//while(multi(chs[ll],chs[1],po)>multi(chs[ll-1],chs[1],chs[ll])&&ll>2)
while(multi(chs[ll],chs[ll-1],po)>0&&ll>1)
{
exans+=xabs(multi(chs[ll],chs[ll-1],po));
ll--;
}
}
printf("%.1lf\n",exans/double(2));
}
}
return 0;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
脑补出AC的方法了。。在凸包内取一个点。对于每次加入都可以logn二分出连到这个点面积增加的最左和最右。然后就三角形面积-凸包内部面积就是答案了。
改到200多行还是wa....估计今天是出不来了
/*结果竟然一周都没怎么写程序。。然后就不想写这题了。总之思路是对的...不过实现还是有细节*/