题意
一张无向图,经过一个点的代价是入边和出边的较大值,起点是出边的值,终点是入边的值。
求 1 到 n 的最短路。
思路
化边为点很显然。但是假如碰到菊花就萎了。
所以下面是一种挺神奇的思路。
- 把无向边拆成两个有向边。
- 每个点的所有出边排序,出边之间,代价小的向代价大的连边权为他们边权差的边,代价大的向小的连边权为 0 的边。
- 对于每一条边 i i i ,假如他从 u u u 走到 v v v ,找到 v v v 的出边中边权比 i i i 大的最小边,连边权差的边。找到边权比 i i i 小的最大边,连 0 的边。
- 最后再新建两个源点汇点,连向 1 和 n 。
这样的话大小关系就不用再判断了,只要跑正常的最短路就好了。
好像叫差分优化建边。我还没听说过。
代码
// bzoj 4289 tax
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<long long, int>
#define fi first
#define se second
#define mp make_pair
typedef long long LL;
const int N = 4e5 + 10, M = N<<2;
const LL inf = 1e18 + 7;
namespace Graph
{
int h[N], ecnt, nxt[M], v[M];
LL w[M];
void clear(){ecnt = 1;}
void add_dir(int _u, int _v, LL _w){
v[++ecnt] = _v; w[ecnt] = _w;
nxt[ecnt] = h[_u]; h[_u] = ecnt;
}
}
using namespace Graph;
int n, m, s, t, beg[N], end[N];
struct edge{
int x, y;
LL z;
}e[N];
priority_queue<pii, vector<pii>, greater<pii> > que;
LL dis[N];
template<class T>inline void read(T &x){
x = 0; bool fl = 0; char c = getchar();
while (!isdigit(c)){if (c == '-') fl = 1; c = getchar();}
while (isdigit(c)){x = (x<<3)+(x<<1)+c-'0'; c = getchar();}
if (fl) x = -x;
}
template<class T>inline void wr(T x){
if (x < 0) x = -x, putchar('-');
if (x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
template<class T>inline void wrl(T x){
wr(x); puts("");
}
bool cmp(edge x, edge y){
if (x.x != y.x) return x.x < y.x;
return x.z < y.z;
}
int get_id(int l, int r, LL val){
int mid, ret = r+1;
while (l <= r){
mid = l+r>>1;
if (e[mid].z >= val) ret = mid, r = mid-1;
else l = mid+1;
}
return ret;
}
LL solve(){
for (int i = 1; i <= m; ++ i) dis[i] = inf;
dis[s] = 0; que.push(mp(dis[s], s));
while (!que.empty()){
int u = que.top().se;
LL d = que.top().fi;
que.pop();
if (d > dis[u]) continue;
for (int i = h[u]; i; i = nxt[i])
if (dis[v[i]] > dis[u]+w[i]){
dis[v[i]] = dis[u]+w[i];
que.push(mp(dis[v[i]], v[i]));
}
}
return dis[t];
}
signed main()
{
read(n); read(m);
for (int i = 1; i <= m; ++ i){
int x, y, z;
read(x); read(y); read(z);
e[(i<<1)-1] = (edge){x, y, z};
e[i<<1] = (edge){y, x, z};
}
m *= 2;
e[++m] = (edge){n+1, 1, -inf};
e[++m] = (edge){n, n+2, -inf};
sort(e + 1, e + m + 1, cmp);
clear();
for (int i = 1, j; i <= m; i = j){
for (j = i; j <= m && e[j].x == e[i].x; ++ j);
int u = e[i].x;
beg[u] = i; end[u] = j-1;
for (int k = i+1; k < j; ++ k){
add_dir(k-1, k, e[k].z-e[k-1].z);
add_dir(k, k-1, 0);
}
}
for (int i = 1; i <= m; ++ i){
int v = e[i].y;
int k = get_id(beg[v], end[v], e[i].z);
if (v == n+2) t = i;
if (e[i].x == n+1) s = i;
if (k <= end[v]) add_dir(i, k, e[k].z);
if (k > beg[v]) add_dir(i, k-1, e[i].z);
}
wrl(solve());
return 0;
}