P6822 [PA2012]Tax

\(\text{Solution}\)

一道有思维的建图题,不妨考虑暴力怎么做,只需枚举形如\(x - y - z\)这样的三个点,把边权赋为最大值即可,但这样边数会很多。
考虑优化,看成有向边,化边为点,对于一个点,把它所连的所有边按权值排序,顺次连接,正向为差值,反向为\(0\),这样就实现了取\(max\)的操作。再把边和它的反向边相连,边权为原边权,这样相当于从一个点走向了边相连的点。
最后建一个超源\(S\),超汇\(T\),跑最短路即可。

\(\text{Code}\)

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define LL long long
using namespace std;
const int N = 1e5 + 5;
int n, tot, m, h[N], hw[N << 2], tw, S, T, vis[N << 2], cnt; LL dis[N << 2];

struct edge{int to, nxt, z, id;}e[N << 2], w[N << 4];
void add(int x, int y, int z, int id){e[++tot] = edge{y, h[x], z, id}, h[x] = tot;}
void addnew(int x, int y, int z){w[++tw] = edge{y, hw[x], z, 0}, hw[x] = tw;}
struct nd{int z, id;}a[N];
bool cmp(nd x, nd y){return x.z < y.z;}
struct qu{
	int x; LL z;
	friend bool operator < (qu x, qu y){return x.z > y.z;}
};
priority_queue<qu> Q;
void dij() {
	memset(dis, 50, sizeof dis);
	dis[S] = 0, Q.push(qu{S, dis[S]});
	while (!Q.empty()) {
		int u = Q.top().x; Q.pop();
		if (vis[u]) continue; vis[u] = 1;
		for (int i = hw[u]; i; i = w[i].nxt) {
			int v = w[i].to;
			if (dis[v] > dis[u] + (LL)w[i].z) 
				dis[v] = dis[u] + (LL)w[i].z, Q.push(qu{v, dis[v]});
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i = 1, q, p, c; i <= m; i++)
		scanf("%d%d%d",&q,&p,&c), add(q, p, c, ++cnt), add(p, q, c, ++cnt),
		addnew(cnt - 1, cnt, c), addnew(cnt, cnt - 1, c);
	S = cnt + 1, T = S + 1;
	for (int i = 1; i <= n; i++) {
		int at = 0;
		for (int j = h[i]; j; j = e[j].nxt) a[++at] = nd{e[j].z, e[j].id};
		sort(a + 1, a + 1 + at, cmp);
		for (int j = 1; j < at; j++)
			addnew(a[j].id, a[j + 1].id, a[j + 1].z - a[j].z), addnew(a[j + 1].id, a[j].id, 0);
	} 
	for (int i = h[1]; i; i = e[i].nxt) addnew(S, e[i].id, e[i].z);
	for (int i = 1; i <= n; i++)
		for (int j = h[i]; j; j = e[j].nxt) 
			if (e[j].to == n) addnew(e[j].id, T, e[j].z);
	dij(), printf("%lld\n", dis[T]);
}
posted @ 2022-10-28 10:57  RiverSheep  阅读(21)  评论(0)    收藏  举报