[CF1473E]Minimum Path【分层图最短路】【dijkstra】

>Link

luogu CF1473E


>Description

给出一张 n n n 个点 m m m 条边的无向图
规定最短路的定义为:这条路径上的权值之和,减去最大权值,加上最小权值
1 1 1 到其他所有的点的最短路

n , m ≤ 2 ∗ 1 0 5 n,m\le 2*10^5 n,m2105


>解题思路

这种在一条路上对路径的权值操作的,想到分层图最短路
考虑最大权值、最小权值怎么搞,发现题意等同于「这条路径上的权值之和,减去一条边的权值,加上一条边权值」,因为这样求最短路,最优的情况肯定是「减去最大权值,加上最小权值」
那我们就把点分成三层,第一层到第二层建权值为 0 的边,表示减去这条边的权值,第二层到第三层建权值为 2 ∗ w 2*w 2w 的边,表示加上这条边的权值。每层内仍然正常建边
然后跑第一层的 1 到第三层的最短路

但是发现这样还是不行,因为这样建图就默认了这条路径的最大值在最小值的前面,还有最大值在最小值的后面的情况
那就第一层和第二层之间、第二层和第三层之间反着建,再跑一次就行了


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 600010
#define LL long long
using namespace std;

struct node
{
	int id; LL ds;
	friend bool operator < (node aa, node bb)
	{
		return aa.ds > bb.ds;
	}
};
priority_queue<node> Q;
struct edge
{
	int to, nxt; LL w;
} e[2000010];
int n, m, cnt, h[N], U[N], V[N];
LL dis[N], ans[N], W[N];
bool vis[N];

void add (int u, int v, LL w)
{
	e[++cnt] = (edge){v, h[u], w};
	h[u] = cnt;
//	e[++cnt] = (edge){u, h[v], w};
//	h[v] = cnt;
}
void dij ()
{
	node u; int v;
	memset (dis, 0x7f, sizeof (dis));
	memset (vis, 0, sizeof (vis));
	Q.push ((node){1, 0}); dis[1] = 0;
	while (!Q.empty())
	{
		u = Q.top();
		Q.pop();
		if (vis[u.id]) continue;
		vis[u.id] = 1;
		for (int i = h[u.id]; i; i = e[i].nxt)
		{
			v = e[i].to;
			if (!vis[v] && dis[u.id] + e[i].w < dis[v])
			{
				dis[v] = dis[u.id] + e[i].w;
				Q.push ((node){v, dis[v]});
			}
		}
	}
	for (int i = 2; i <= n; i++)
	{
		ans[i] = min (ans[i], dis[i + 2 * n]);
		ans[i] = min (ans[i], dis[i]);
	}
}

int main()
{
	int l;
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf ("%d%d%lld", &U[i], &V[i], &W[i]);
		for (int j = 1; j <= 3; j++)
		{
			l = (j - 1) * n;
			add (l + U[i], l + V[i], W[i]);
			add (l + V[i], l + U[i], W[i]);
		}
		add (U[i], V[i] + n, 0);
		add (V[i], U[i] + n, 0);
		add (U[i] + n, V[i] + 2 * n, 2 * W[i]);
		add (V[i] + n, U[i] + 2 * n, 2 * W[i]);
	}
	memset (ans, 0x7f, sizeof (ans));
	dij ();
	cnt = 0;
	memset (h, 0, sizeof (h));
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= 3; j++)
		{
			l = (j - 1) * n;
			add (l + U[i], l + V[i], W[i]);
			add (l + V[i], l + U[i], W[i]);
		}
		add (U[i], V[i] + n, 2 * W[i]);
		add (V[i], U[i] + n, 2 * W[i]);
		add (U[i] + n, V[i] + 2 * n, 0);
		add (V[i] + n, U[i] + 2 * n, 0);
	}
	dij ();
	for (int i = 2; i <= n; i++)
	  printf ("%lld ", ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值