题解:CF1473 E. Minimum Path

题面

Translation

有一条路径上共有 \(k\) 条边,第 \(i\) 条边边权为 \(w_i\) ,定义该路径权值为 \(\sum\limits_{i=1}^{k}{w_i} - \max\limits_{i=1}^{k}{w_i} + \min\limits_{i=1}^{k}{w_i}\) ,给一张 \(n\) 条边, \(m\) 条边的无向连通图,求从 \(1\) 号点到 \(i(2\leq i \leq n)\) 号点路径权值的最小值。

Solution

容易发现对于一条路径而言,该式子的含义就是将最大的一条边权改成 0 ,将最小的一条边权翻成两倍。

再进一步思考,若是把式子改为 \(\sum\limits_{i=1}^{k}{w_i} - w_p + w_q\) ,其中 \(p\) , \(q\) 是任意两条边,那么要求的答案就是改完后的式子的最小值。

发现有两步特殊操作,一步是将一个边权减一,另一步是将一个边权翻倍。

可以使用分层图跑最短路解决。

发现两步操作没有先后顺序,所以要开 4 层图,第 1 层放原图,第 2 层放已经有被清零的边权的图,第 3 层放已经有翻倍边权的图,最后一层就是答案。

对于一个点 \(p\) ,约定在第 \(i\) 层的序号表示为 \(p+(i-1)\times n\)

具体连边操作(对于一条 \(u \longrightarrow v\) ,权值为 \(w\) 的原图边):

\(u \longrightarrow v\) 边权为 \(w\)

\(u \longrightarrow v+n\) 边权为 \(0\)

\(u \longrightarrow v+n\times 2\) 边权为 \(w\times 2\)

\(u \longrightarrow v+n\times 3\) 边权为 \(w\) (路径上边数为 1 的特殊情况)

\(u+n \longrightarrow v+n\) 边权为 \(w\)

\(u+n \longrightarrow v+n\times 3\) 边权为 \(w\times 2\)

\(u+n\times 2 \longrightarrow v\times 2\) 边权为 \(w\)

\(u+n\times 2 \longrightarrow v\times 3\) 边权为 \(0\)

\(u+n\times 3 \longrightarrow v\times 3\) 边权为 \(w\)

Code(C++):

// E. Minimum Path
#include<bits/stdc++.h>
#define forn(i,s,t) for(register int i=(s);i<=(t);++i)
using namespace std;
typedef long long LL;
const int N = 8e5+3, M = 1e7+3;
struct List{int dir, nxt; LL lng;} E[M];
int G[N];
inline void Add(int u, int v, LL w) {
	static int cnt;
	E[++cnt] = (List){v, G[u], w}; G[u] = cnt;
}
int n, m; LL dis[N];
int main() {
	scanf("%d%d", &n, &m);
	forn(i,1,m) {
		static int u, v; static LL w;
		scanf("%d%d%lld", &u, &v, &w);
		Add(u, v, w), Add(v, u, w);
		Add(u, v+n, 0), Add(v, u+n, 0);
		Add(u, v+n*2, w<<1), Add(v, u+n*2, w<<1);
		Add(u, v+n*3, w), Add(v, u+n*3, w);
		Add(u+n, v+n, w), Add(v+n, u+n, w);
		Add(u+n, v+n*3, w<<1), Add(v+n, u+n*3, w<<1);
		Add(u+2*n, v+2*n, w), Add(v+2*n, u+2*n, w);
		Add(u+2*n, v+3*n, 0), Add(v+2*n, u+3*n, 0);
		Add(u+3*n, v+3*n, w), Add(v+3*n, u+3*n, w);
	}
	memset(dis, -1, sizeof dis);
	static priority_queue<pair<LL, int> > q;
	q.push(make_pair(0ll, 1));
	while(!q.empty()) {
		static int u;
		u = q.top().second;
		if(dis[u] != -1) {q.pop(); continue ;}
		dis[u] = - q.top().first; q.pop();
		for(int i=G[u];i;i=E[i].nxt) 
			if(dis[E[i].dir] == -1) 
				q.push(make_pair(-dis[u]-E[i].lng, E[i].dir));
	}
	forn(i,2,n) printf("%lld ", dis[i + 3*n]);
	return 0;
}

Edu Round 的质量很高,但是不会网络流和 FFT ,这场的 F 、 G 只能以后再补了。。。

posted @ 2021-02-01 10:35  AxDea  阅读(106)  评论(0编辑  收藏  举报