Description

nn个城镇,城镇之间共有 mm 条双向道路,每条道路有三个属性 uiu_i,viv_icic_i,uiu_iviv_i 表示它连 接的城镇,cic_i 表示费用。当土匀匀经过一个城镇的时候(包括起点和终点),费用为进入这个城镇的道路和出这个城镇的道路费用的较大值。 土匀匀想花最少的钱去看病,但他不会编程,于是他找到了聪明的你,希望你能帮他解决这个问题。

Constraints

2n100000,1m2000002\le n\le100000,1\le m\le200000

Solution

考虑把边看成点,在原图中一个点连出去的边,两两枚举在新图中的点上建边,边权就可以直接取max。但是这显然是O(m2)O(m^2)

因为一条边的权值,可以直接连来得到,也可以差分累加得到。考虑差分建图,把原图中一个点连出的边集排序后,从小到大建w[j]w[i]w[j]-w[i]i>ji->j的边,并从j>ij->i建长度为0的边。对于原图中的一条双向边,拆成两个点,相互连权值为原边权值的边。对起点终点特殊搞一下

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <bits/stdc++.h>

#define x first
#define y second
#define x1 X1
#define x2 X2
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair<int, int> pii;

template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; }
namespace pb_ds
{
namespace io
{
const int MaxBuff = 1 << 15;
const int Output = 1 << 24;
char B[MaxBuff], *S = B, *T = B;
#define getc() ((S == T) && (T = (S = B) + fread(B, 1, MaxBuff, stdin), S == T) ? 0 : *S++)
char Out[Output], *iter = Out;
inline void flush()
{
fwrite(Out, 1, iter - Out, stdout);
iter = Out;
}
}

template<class Type> inline Type read()
{
using namespace io;
register char ch; register Type ans = 0; register bool neg = 0;
while(ch = getc(), (ch < '0' || ch > '9') && ch != '-') ;
ch == '-' ? neg = 1 : ans = ch - '0';
while(ch = getc(), '0' <= ch && ch <= '9') ans = ans * 10 + ch - '0';
return neg ? -ans : ans;
}
};

using namespace pb_ds;

const int Maxn = 4e5 + 100, Maxm = 2e6 + 100;
const LL inf = (LL)0x3f3f3f3f3f3f3f3f;

int N, M, e, Begin[Maxn], To[Maxm], Next[Maxm], W[Maxm];

inline void add_edge (int x, int y, int z)
{
To[++e] = y;
Next[e] = Begin[x];
Begin[x] = e;
W[e] = z;
}

struct node
{
int a;
LL b;
bool operator < (const node &x) const
{
return x.b < b;
}
};

priority_queue <node> Q;

LL Dis[Maxn];
int Vis[Maxn], cnt;

inline void Dijkstra(int S)
{
for (int i = 1; i < Maxn; ++i) Dis[i] = inf;
Dis[S] = 0;
Q.push((node){S, 0});
while (!Q.empty())
{
node tmp = Q.top(); Q.pop();
int x = tmp.a;
if (Vis[x]) continue;
Vis[x] = 1;
for (int i = Begin[x]; i; i = Next[i])
{
int y = To[i];
if (Dis[y] > Dis[x] + W[i])
{
Dis[y] = Dis[x] + W[i];
Q.push((node){y, Dis[y]});
}
}
}
}

struct edge
{
int x, y, z, id;
bool operator < (const edge &a) const
{
if (x == a.x) return z < a.z;
return x < a.x;
}
}E[Maxm];

int main()
{
#ifndef ONLINE_JUDGE
freopen("illness.in", "r", stdin);
freopen("illness.out", "w", stdout);
#endif
N = read<int>(), M = read<int>();
int S = Maxn - 10;
for (int i = 1; i <= M; ++i)
{
int x = read<int>(), y = read<int>(), z = read<int>();
add_edge (i, i + M, z);
add_edge (i + M, i, z);
if (x == 1) add_edge (S, i, z), add_edge (i, S, z);
else if (y == 1) add_edge (S, i + M, z), add_edge (i + M, S, z);
E[i * 2 - 1].x = x, E[i * 2 - 1].y = y, E[i * 2 - 1].z = z, E[i * 2 - 1].id = i;
E[i * 2].x = y, E[i * 2].y = x, E[i * 2].z = z, E[i * 2].id = i + M;
}
sort(E + 1, E + 2 * M + 1);
int j = 1;
for (int i = 1; i <= N; ++i)
{
if (E[j].x > i) continue;
while (E[j + 1].x == i)
{
add_edge (E[j].id, E[j + 1].id, E[j + 1].z - E[j].z);
add_edge (E[j + 1].id, E[j].id, 0);
++j;
}
++j;
}
Dijkstra(S);
LL ans = inf;
for (int i = 1; i <= 2 * M; ++i) if (E[i].x == N || E[i].y == N) Chkmin(ans, Dis[E[i].id] + E[i].z);
cout<<ans<<endl;
return 0;
}