JZOJ 3762. 【NOIP模拟 8.12】过河(river)

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<=10^5,wi<=10^5,vi<=10^5,Y<=10^5。

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 <bits/stdc++.h>

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9')  {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

struct NOTE
{
    double t;
    int x,y,z;

    bool operator < (NOTE a) const
    {
        return a.t < t;
    }
};

const int N = 50005;

int w[N],v[N];
int y,u,n;

double ans;

std::priority_queue <NOTE> Q;

double getDis(double x, double y)
{
    return sqrt(x * x + y * y);
}

int main()
{
    n = read(), y = read(), u = read();
    for (int i = 1; i <= n; i++)
        w[i] = read();
    for (int i = 1; i <= n; i++)
        v[i] = read();
    NOTE p;
    ans = 0;
    for (int i = 1; i <= n; i++)
    {
        ans += w[i] / (double)v[i];
        p.t = (getDis(w[i], 1) - w[i]) / (double)v[i];
        p.x = w[i], p.y = 0, p.z = v[i];
        Q.push(p);
    }
    p.t = 1 / double(u);
    p.x = 0, p.y = 0, p.z = u;
    Q.push(p);
    for (int i = 1; i <= y; i++)
    {
        p = Q.top(), Q.pop();
        ans += p.t;
        p.y++;
        p.t = (getDis(p.x, p.y + 1) - getDis(p.x, p.y)) / (double)p.z;
        Q.push(p);
    }
    printf("%.4f\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值