Description
有(N+1)个平行于y轴的河岸排成一排,每两个河岸之间夹着一条河,所以一共有N条河。第i 条河的宽度为wi,ryz在第i 条河中行进的速度为vi。河岸的宽度忽略不计。令X=sigma(wi)。
规定:
1、ryz从(0,0)出发,终点是(X,Y)。Y是一个给定的整数。
2、ryz在渡河时,必须从一个整点驶向另一个整点,花费的时间为这两个点的欧几里得距离除以速度。
3、ryz可以在河岸上行走,但也是必须从一个整点走向另一个整点,速度为给定的u。
求花费时间的最小值。
Input
第一行三个正整数N,Y,u。
第二行N个正整数表示wi。
第三行N个正整数表示vi。
Output
输出一行答案,保留4位小数。
Sample Input
2 3 1
5 5
1 1
Sample Output
10.4842
Data Constraint
对于30%的数据,N<=50,Y<=500;
对于60%的数据,N<=50;
对于100%的数据,N<=50000;
对于100%的数据,u<=105,wi<=105,vi<=105,Y<=105。
Hint
样例解释
Ryz先从(0,0)渡过第一条河到达(5,1),花费时间为5.0990,然后渡过第二条河到达(10,3),花费时间为5.3852。
先到(5,2)再到(10,3)也一种最优解。
思路
如果现在让你找一条路线(不一定最优),你一定会选择先向下走过N行后向右走到第Y列,如图。总时间可以马上计算出来。
这时候如果将路线分段,每一段为路线中其中一行到下一行的路线,可以考虑让其中一段路稍稍改变,让总时间减少。为什么要这样考虑?因为改变这一段路线其实并不改变其他N-1段路线的长度以及时间(显然)。怎么改变?当然是将下一行上的端点向右移动1个单位(为什么不能向左移动?显然这只会增加时间):
这样改变对总时间的影响是多少呢?因为上图中这条绿色的倾斜的路线实际上替代了2段粉色的路线,因此对总时间的增加或减少是可以计算出来的(怎么计算??可以用走绿色斜线的时间减去走原路线即2段粉色路线的时间)。如果总时间减少了那么这次路线改变就是成功的。对于N段路线我们都可以考虑这样的改变,最多可以改变Y次(为什么?请仔细思考)。
那么一个基于贪心思想的解法就出来了:每一次在N段路线中选择1条改变路线后对总时间影响最大的路线进行改变,直至改变了Y次或者没有能减少时间的改变为止。由于实际上要在N个数中找一个最小的数并且进行修改,因此可以用一个堆维护这N个数,在O(logN)的时间内就可以完成一次修改,总的时间复杂度为O(YlogN)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1e5+77;
struct A
{
int w,v,y;
double t;
}a[maxn];
int n,y,u,w[maxn],v[maxn],cnt;
double ans;
double dis(int x,int y)
{
return sqrt(x*x+y*y);
}
void up(int x)
{
while(x/2>0)
{
if(a[x/2].t>a[x].t) swap(a[x/2].t,a[x].t); else break;
x>>=1;
}
}
void down(int x)
{
int t=x;
while(t*2<=cnt)
{
int p=t*2;
if(p+1<=cnt&&a[p].t>a[p+1].t) p++;
if(a[t].t>a[p].t) swap(a[t],a[p]);else break;
t=p;
}
}
void ins(A x)
{
a[++cnt]=x; up(cnt);
}
void pop()
{
swap(a[1],a[cnt]); cnt--; down(1);
}
A top()
{
return a[1];
}
int main()
{
scanf("%d%d%d",&n,&y,&u);
for(int i=1; i<=n; i++) scanf("%d",&w[i]);
for(int i=1; i<=n; i++) scanf("%d",&v[i]);
for(int i=1; i<=n+2; i++) a[i].t=2147483647;
for(int i=1; i<=n; i++)
{
A p;
p.t=(dis(1,w[i])-w[i])/(double)v[i];
ans+=(double)w[i]/v[i];
p.w=w[i]; p.v=v[i]; p.y=0;
ins(p);
// printf("w=%d v=%d w[i]/v[i]=%.7lf s=%.7lf\n",w[i],v[i],(double)w[i]/v[i],ans);
}
A p;
p.t=(double)1/u; p.w=0; p.v=u; p.y=0; ins(p);
for(int i=1; i<=y; i++)
{
A p=top(); pop();
ans+=p.t;
// printf("t=%.7lf s=%.7lf\n",p.t,ans);
p.y++;
p.t=(double)(dis(p.w,p.y+1)-dis(p.w,p.y))/p.v;
ins(p);
}
printf("%.4lf",ans);
}