题目:传送门
思路:想要走最短距离,肯定是两点之间的曼哈顿距离最短,并且走过最多的喷泉。因为是最短,所以只能往终点方向移动,求一下起点到终点之间喷泉y坐标的最大单调序列,就能知道最多经过多少喷泉。有一种特殊情况,就是起点到终点的每一行或者每一列都有可走的喷泉(如下图),这样就要多算1/4个圆周。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define N 200005
#define LL long long
#define PI asin(1)*2
using namespace std;
struct point{
int x,y;
point(){};
point(int x,int y){
this->x=x;
this->y=y;
}
};
point s,t;
int n;
int dp[N],x[N];
vector<point> p;
int cmp(point a,point b)
{
return a.x<b.x;
}
int main()
{
//cout<<PI<<endl;
while(~scanf("%d%d%d%d",&s.x,&s.y,&t.x,&t.y))
{
if(s.x>t.x) swap(s,t);
point ld=point(s.x,min(s.y,t.y));
point ru=point(t.x,max(t.y,s.y));
scanf("%d",&n);
point tmp;
while(n--)
{
scanf("%d%d",&tmp.x,&tmp.y);
if(tmp.x>=ld.x&&tmp.x<=ru.x&&tmp.y>=ld.y&&tmp.y<=ru.y)
p.push_back(tmp);
}
sort(p.begin(),p.end(),cmp);
double ans;
ans=abs(s.x-t.x)+abs(s.y-t.y);
ans*=100;
if(s.x==t.x||s.y==t.y)
{
if(p.size()!=0)
ans=ans-10*2+10*PI;
}
else if(s.y<t.y&&p.size()>0)
{
int maxn;
x[1]=p[0].y;
int k=1;
dp[0]=1;
for(int i=1;i<p.size();i++)
{
if(x[k]<p[i].y)
{
x[++k]=p[i].y;
dp[i]=k;
}
else
{
int l=1,r=k;
while(l<=r)
{
int mid=(l+r)/2;
if(x[mid]<=p[i].y)
l=mid+1;
else
r=mid-1;
}
dp[i]=l;
x[l]=p[i].y;
}
}
maxn=k;
if(maxn==abs(s.x-t.x)+1||maxn==abs(s.y-t.y)+1)
ans+=5*PI;
ans=ans-20*maxn+5*PI*maxn;
}
else if(s.y>t.y&&p.size()>0)
{
int maxn;
x[1]=p[p.size()-1].y;
int k=1;
dp[p.size()-1]=1;
for(int i=p.size()-2;i>=0;i--)
{
if(x[k]<p[i].y)
{
x[++k]=p[i].y;
dp[i]=k;
}
else
{
int l=1,r=k;
while(l<=r)
{
int mid=(l+r)/2;
if(x[mid]<=p[i].y)
l=mid+1;
else
r=mid-1;
}
dp[i]=l;
x[l]=p[i].y;
}
}
maxn=k;
if(maxn==abs(s.x-t.x)+1||maxn==abs(s.y-t.y)+1)
ans+=5*PI;
ans=ans-20*maxn+5*PI*maxn;
}
printf("%.15lf\n",ans);
p.clear();
}
}