AtCoder Beginner Contest 204 赛后补题

E - Rush Hour 2

题目链接:https://atcoder.jp/contests/abc204/tasks/abc204_e

题意

给定一张无向图,一个人时刻 t t t从一个点走到另一个点需要使用 C i + ⌊ D i t + 1 ⌋ C_i + \lfloor \frac{D_i}{t + 1} \rfloor Ci+t+1Di的时间。起点是1号点,终点是n号点,起始时刻是0,可以在某一点等待任意秒后再出发。求从起点到终点的最短时间。

思路

设到达 u u u点的时刻为 t t t,下一个点是 v v v,设在u点等待了 x x x秒,那么到下一个点的时刻
f ( x ) = x + C i + ⌊ D i t + 1 + x ⌋ f(x) = x + C_i + \lfloor \frac{D_i}{t + 1 + x} \rfloor f(x)=x+Ci+t+1+xDi, 其中 C i C_i Ci D i D_i Di t t t是常数。看到这个函数, 很容易陷入一个误区,那就是认为 f ( x ) f(x) f(x)是一个凸函数(对勾函数),然后三分求解。但实际上由于取整函数的原因, f ( x ) f(x) f(x)不连续,真实的 f ( x ) f(x) f(x)的图像如下:AtCoder Beginner Contest 204 赛后补题_第1张图片
真实的函数由若干斜率为1的直线构成,所以正确的做法应该是取 x = D i − t − 1 x=\sqrt{D_i}-t-1 x=Di t1(均值不等式),然后在x的邻域搜索最小的 t t t,然后跑一边堆优化迪杰斯特拉即可。

代码如下:

#include
using namespace std;
const int N = 2e5 + 5;
#define int long long
int e[N], h[N], idx, c[N], d[N], dist[N], ne[N];
bool st[N];
using pii = pair<int, int>;
void add(int a, int b, int C, int D){
    e[idx] = b, c[idx] = C, d[idx] = D, ne[idx] = h[a], h[a] = idx++;
}
void dij(){
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<pii, vector<pii>, greater<pii> >q;
    q.push({0, 1});
    while(q.size()){
        int t = q.top().second;
        q.pop();
        if(st[t]) continue;
        st[t] = 1;
        for(int i = h[t];~i;i = ne[i]){
            int j = e[i];
            int x = c[i] + d[i] / (dist[t] + 1);
            for (int _ =  max((int)sqrt(d[i]) - 3 - dist[t], 0ll); _ <= sqrt(d[i]) + 3 - dist[t]; ++ _){
                if (_ < dist[t]) continue;
                x = min(_ + c[i] + d[i] / (_ + dist[t] + 1), x);
            }
                if(dist[j] > dist[t] + x){
                dist[j] = x + dist[t];
                q.push({dist[j], j});
            }
        }
    }
}
signed main(){
    memset(h, -1, sizeof h);
    int n, m;
    scanf("%lld%lld", &n, &m);
    for(int i = 0;i < m; i++){
        int a, b, C, D;
        scanf("%lld%lld%lld%lld", &a, &b, &C, &D);
        add(a, b, C, D);
        add(b, a, C, D);
    }
    dij();
    cout << (dist[n] == 0x3f3f3f3f3f3f3f3f ? -1 : dist[n]);
    return 0;
}X

你可能感兴趣的:(图论,数学)