JSOI2014第三轮 DAY2T2 士兵部署

// 完全搞不懂版权到底是个什么东西..不过如果内容侵犯版权的话请提醒我删除=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....估计今天是出不来了


/*结果竟然一周都没怎么写程序。。然后就不想写这题了。总之思路是对的...不过实现还是有细节*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值