【CodeForces 1473E】Minimum Path
链接:
题目大意:
给定一张 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边边权为 \(w_i\)。
对于一条点 \(1\) 到点 \(n\) 的路径,设其经过的边集为 \(E\),定义这条路径的长度为
\[\sum_{e\in E}w_e-\max_{e\in E}(w_e)+\min_{e\in E}(w_e)
\]
求点 \(1\) 到其他所有点的最短路。
思路:
这种路径长度中涉及最大最小,没法直接转移,考虑将题目变形。题目求 \(\min\left\{\sum_{e\in E}w_e-\max_{e\in E}(w_e)+\min_{e\in E}(w_e)\right\}\),如果要使得长度最小,那么减掉的一定是最大,加上的一定是最小,所以这个题目好像在说废话,转化为长度之和加上、减去任意某边的最小值。
接下来实现加减操作可以建立分层图,每层都是正常连边,第一层到第二层连边权为 \(0\) 的边,第二层到第三层连边权为 \(2w\) 的边,答案就是从 \(1\) 到第三层的各个点。
但这么做忽略了先加后减的情况,不妨再建一层,第一层到本层连边权为 \(2w\) 的边,本层向终点层连边权为 \(0\) 的边。
代码:
const int N = 1e6 + 10;
inline ll Read() {
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, m, T;
struct edge {
int to, nxt; ll val;
}e[N * 3 << 1];
int head[N], tot;
void add (int u, int v, ll w) {
e[++tot] = (edge) { v, head[u], w }, head[u] = tot;
}
struct node {
int val; ll key;
bool operator < (const node &a) const {
return key > a.key;
}
};
priority_queue <node> q;
ll dis[N];
bool vis[N];
void dij () {
memset (dis, 127 / 3, sizeof dis);
dis[1] = 0;
q.push((node){ 1, 0 });
while (!q.empty()){
int u = q.top().val; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (dis[v] > dis[u] + e[i].val) {
dis[v] = dis[u] + e[i].val;
q.push((node) { v, dis[v] });
}
}
}
}
int main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
n = Read(), m = Read();
for (int i = 1; i <= m; i++) {
int u = Read(), v = Read(); ll w = Read();
add (u, v, w), add(v, u, w);
add (u + n, v + n, w), add(v + n, u + n, w);
add (u + 2 * n, v + 2 * n, w), add (v + 2 * n, u + 2 * n, w);
add (u + 3 * n, v + 3 * n, w), add (v + 3 * n, u + 3 * n, w);
add (u, v + n, 2 * w), add (v, u + n, 2 * w);
add (u + n, v + 3 * n, 0), add(v + n, u + 3 * n, 0);
add (u, v + 2 * n, 0), add (v, u + 2 * n, 0);
add (u + 2 * n, v + 3 * n, 2 * w), add (v + 2 * n, u + 3 * n, 2 * w);
}
dij();
for (int i = 2; i <= n; i++)
printf ("%lld ", min(dis[i], dis[i + 3 * n]));
return 0;
}