2015.6.24 XJOI T1.不可视境界线

背景:

ZxrtX0IEEJCABCUhAAr1Y4NEMtL24Q9V0CUhAAhK

题目描述:

 

AMEQHlEbONAWAAAAAElFTkSuQmCC

输入格式:

543PDPxEEAAAQQQQAABowRo6IwqJ2EQQAABBBBAw

输出格式:

wGn+ZzdFBPBAgAAAABJRU5ErkJggg==

样例输入:

4
1 0
2 0
3 0
4 0

样例输出:

2.0000

数据范围:

wOPI63ulvVR5gAAAABJRU5ErkJggg==

时间限制:

1s

 

空间限制:

512MB


考试的时候只想到了cdq分治...没想到b的处理方法所以写了40分的结果拼上去的暴力挂了只剩下3 4的20分了

其实这题cdq分治的确可做

首先题目那个-a是忽悠你的。可以直接去掉

那么转移方程就是f[i]=min(f[i],sqrt(f[j]*f[j]+(a[i]-a[j])*(a[i]-a[j]))

我们把a[i]降序排序

然后移项后如果j>k j比k对于i优的话,那么满足((f[j]*f[j]+a[j]*a[j])+(f[k]*f[k]+a[k]*a[k]))/(2*(a[j]-a[k]))>a[i]

我们cdq分治处理左右区间。保证转移右区间的时候左区间已经转移完毕

然后左区间按照a降序,右区间按照b降序

这样就可以在枚举到右区间某个数的时候把可行左区间加入队列

然后对已经加入左区间的斜率二分就可以了

#include<cmath>
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
struct enemy
{
     double a,b;
     double f;
     int p;
}x[100001],tx[100001];
int n;
double f[100001];
int q[100001];
inline bool cmp1(enemy x,enemy y)
{
     return x.b>y.b;
}
inline bool cmp2(enemy x,enemy y)
{
     return x.a<y.a;
}
inline double getk(int j,int k)
{
	 if(j==0&&k==0)
	      return -1;
     return ((x[j].f*x[j].f+x[j].a*x[j].a)-(x[k].f*x[k].f+x[k].a*x[k].a))/(double(2)*(x[j].a-x[k].a));
}
inline void cdq(int ll,int rr)
{
     if(ll!=rr)
     {
          int mid=(ll+rr)/2;
          int i;
          int p1=ll-1,p2=mid;
          for(i=ll;i<=rr;i++)
          {
               if(x[i].p<=mid)
               {
                    p1++;
                    tx[p1]=x[i];
               }
               else
               {
                    p2++;
                    tx[p2]=x[i];
               }
          }
          for(i=ll;i<=rr;i++)
               x[i]=tx[i];
          cdq(ll,mid);
          sort(x+mid+1,x+rr+1,cmp1);
          int r=0;
          int pp=ll;
          for(i=mid+1;i<=rr;i++)
          {
               while(x[pp].a>=x[i].b&&pp<=mid)
               {
                    while(1<r&&getk(q[r],q[r-1])<getk(pp,q[r]))
                         r--;
                    r++;
                    q[r]=pp;
                    pp++;
               }
               int l0=2,r0=r;
               while(l0<=r0)
               {
                    int mid=(l0+r0)/2;
                    if(getk(q[mid],q[mid-1])>=x[i].a)
                         l0=mid+1;
                    else
                         r0=mid-1;
               }
               if(r0!=0)
                    x[i].f=min(x[i].f,sqrt(x[q[r0]].f*x[q[r0]].f+(x[q[r0]].a-x[i].a)*(x[q[r0]].a-x[i].a)));
          }
          cdq(mid+1,rr);
          p1=ll;
		  p2=mid+1;
          int p=ll-1;
          while(p1<=mid&&p2<=rr)
          {
               if(x[p1].a>x[p2].a)
               {
                    p++;
                    tx[p]=x[p1];
                    p1++;
               }
               else
               {
                    p++;
                    tx[p]=x[p2];
                    p2++;
               }
          }
          while(p1<=mid)
          {
               p++;
               tx[p]=x[p1];
               p1++;
          }
          while(p2<=rr)
          {
               p++;
               tx[p]=x[p2];
               p2++;
          }
          for(i=ll;i<=rr;i++)
               x[i]=tx[i];
     }
}
inline void solve2()
{
     cdq(1,n);
     int i;
     for(i=1;i<=n;i++)
          if(x[i].p==n)
               break;
     printf("%.4lf\n",x[i].f);
}
int main()
{
     scanf("%d",&n);
     int i;
     bool flag=true;
     for(i=1;i<=n;i++)
     {
          scanf("%lf%lf",&x[i].a,&x[i].b);
          x[i].p=i;
          if(x[i].b==0)
               x[i].f=x[i].a;
          else
               x[i].f=1000000000;
          if(x[i].b!=0)
               flag=false;
     }
          solve2();
     return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值