【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;
}

posted @ 2021-11-18 18:40  Jayun  阅读(69)  评论(0编辑  收藏  举报