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 (i−j)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]+(i−j)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;
}