Codeforces Round #816 (Div. 2)

Codeforces Round #816 (Div. 2)

传送门: Codeforces Round #816 (Div. 2)

参考视频:https://www.bilibili.com/video/BV1ZG41147HH

E. Long Way Home

prob:

n个点m条无向边,同时定义两点间无向特殊边的代价为这两点标号的平方,即 ( i − j ) 2 (i - j)^2 (ij)2,问从1号点出发到每个点且经过特殊边不超过k次的最短路

n,m 1e5, k 20 ,边权w 1e9 均为正数

ideas:

如果不考虑特殊边,直接dijkstra

如果最多经过一条特殊边,

考虑最后一条边一定是特殊边的最短路,在刚开始dijkstra的dist数组基础上dp, d p [ i ] = m i n { d i s t [ j ] + ( i − j ) 2 } dp[i]= min\{dist[j] + (i - j)^2\} dp[i]=min{dist[j]+(ij)2}

转移次数太多了,考虑优化,可以发现当j < i 的时候 j 的取值具有决策单调性(可打表得出,

于是这部分可以分治完成, s o l v e ( l , r , x , y ) solve(l, r, x, y) solve(l,r,x,y)表示当前正在处理 [ l , r ] [l, r] [l,r],决策可能的区间为 [ x , y ] [x, y] [x,y]

已知最多经过k条特殊边且最后一次是特殊边的情况,扩展到一般情况(即不一定最后一次是特殊边)

在经过k条的基础上进行dijkstra

已知最多经过k条特殊边的情况,求最多经过k+1条特殊边,由刚开始最多经过一条特殊边的情况扩展即可

code:

刚开始dijkstra里面dist和i写反了,de了一个世纪,我不明白这样错为什么还能过81个点

注意dijkstra里面每个都要放进去,不过如果没有每个放的话样例也体现了,样例挺友好的w

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;
typedef pair<int, ll> pii;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll N = 1e5 + 10;

struct node {
    int to;
    ll len;
};

vector<node> g[N];
int n, m, k;
ll dist[N]; // the min dist
ll f[N]; // the min dist with last time is a flight

void dij() {
    priority_queue<pii, vector<pii>, greater<> > que;

    for (int i = 1; i <= n; ++i) {
        que.push({dist[i], i});
    }

    while (!que.empty()) {
        auto[dis, u] = que.top();
        que.pop();

        if (dis > dist[u]) continue;

        for (auto[to, len] : g[u]) {
            if (dist[u] + len < dist[to]) {
                dist[to] = dist[u] + len;
                que.push({dist[to], to});
            }
        }
    }
}

ll cal(int x, int p) {
    return dist[p] + (ll) ((ll) (p - x) * (ll) (p - x));
}

void solve(int l, int r, int x, int y) { // cal f , divide and conquer
    if (l > r) return;
    int mid = (l + r) >> 1;
    int p = x;
    ll mn = inf;
    for (int i = x; i <= y && i <= mid - 1; ++i) {
        ll tmp = cal(mid, i);
        if (tmp < mn) {
            mn = tmp;
            p = i;
        }
    }
    f[mid] = min(f[mid], cal(mid, p));
//    cerr << mid << " " << p << endl;
    solve(l, mid - 1, x, p), solve(mid + 1, r, p, y);
}

signed main() {
    
    scanf("%d%d%d", &n, &m, &k);

    for (int i = 1, u, v, len; i <= m; ++i) {
        scanf("%d%d%d", &u, &v, &len);
        g[u].push_back({v, len});
        g[v].push_back({u, len});
    }

    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    dij();

    for (int kkk = 0; kkk < k; ++kkk) {
        memset(f, 0x3f, sizeof f);

        solve(1, n, 1, n);

        reverse(dist + 1, dist + n + 1);
        reverse(f + 1, f + n + 1);

        solve(1, n, 1, n);

        reverse(dist + 1, dist + n + 1);
        reverse(f + 1, f + n + 1);

        for (int i = 1; i <= n; ++i) {
            dist[i] = min(dist[i], f[i]);
        }

        dij();
    }

    for (int i = 1; i <= n; ++i) {
        printf("%lld ", dist[i]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值