nn个点mm条边的无向带权图,qq次询问(x,y)(x,y),每次询问求xxyy所有的路径中最小权值的最大值

n10000,m50000,q30000n\le 10000, m \le 50000, q\le 30000

Luogu P1967

Solution

这题好裸啊...

显然答案一定在最大生成树上

倍增/树剖维护任意两点路径最小值即可

要注意算答案的时候的各种奇奇怪怪的边界情况,每次写这种树上倍增维护最大值的都会写挂几次...

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
#include <bits/stdc++.h>

#define x first
#define y second
#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;

inline void proc_status ()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) <<endl;
}

template <typename T> inline int Chkmin (T &a, T b) {return a > b ? a = b, 1 : 0; }
template <typename T> inline int Chkmax (T &a, T b) {return a < b ? a = b, 1 : 0; }

inline int read ()
{
int sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

const int Maxn = 10000 + 10, Maxm = 50000, inf = 0x3f3f3f3f;

int N, M, Q;

struct edge
{
int x, y, z;
}E[Maxm];

inline int cmp (edge a, edge b) { return a.z > b.z; }

namespace DSU
{
int fa[Maxn];
inline void init () { for (int i = 1; i <= N; ++i) fa[i] = i; }
inline int find (int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
inline void link (int x, int y) { fa[find(y)] = find(x); }
}

int e, Begin[Maxn], To[Maxm << 1], Next[Maxm << 1], W[Maxm << 1];

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

int anc[22][Maxn], dep[Maxn], Min[22][Maxn];

inline void dfs (int x)
{
dep[x] = dep[anc[0][x]] + 1;
for (int i = 1; i <= 20; ++i)
{
anc[i][x] = anc[i - 1][anc[i - 1][x]];
Min[i][x] = min(Min[i - 1][x], Min[i - 1][anc[i - 1][x]]);
}
for (int i = Begin[x]; i; i = Next[i])
{
int y = To[i];
if (y == anc[0][x]) continue ;
Min[0][y] = W[i];
anc[0][y] = x;
dfs(y);
}
}

inline int Get_ans (int x, int y)
{
int ans = inf;
if (dep[x] < dep[y]) swap(x, y);
for (int i = 20; i >= 0; --i)
if (dep[anc[i][x]] >= dep[y]) Chkmin(ans, Min[i][x]), x = anc[i][x];
if (x == y) return ans;
Chkmin (ans, min(Min[0][x], Min[0][y]));
for (int i = 20; i >= 0; --i)
if (anc[i][x] != anc[i][y]) Chkmin(ans, Min[i][x]), Chkmin(ans, Min[i][y]), x = anc[i][x], y = anc[i][y];
Chkmin (ans, min(Min[0][x], Min[0][y]));
return ans;
}

inline void Solve ()
{
sort (E + 1, E + M + 1, cmp);
DSU :: init();

for (int i = 1; i <= M; ++i)
{
int x = E[i].x, y = E[i].y, z = E[i].z;
if (DSU :: find(x) == DSU :: find(y)) continue;
add_edge (x, y, z);
add_edge (y, x, z);
DSU :: link (x, y);
}

for (int s = 1; s <= N; ++s)
if (!dep[s])
{
for (int i = 0; i <= 20; ++i) Min[i][s] = inf;
dfs(s);
}

Q = read();
while (Q--)
{
int x = read(), y = read();
if (DSU :: find(x) != DSU :: find(y)) printf("-1\n");
else printf("%d\n", Get_ans(x, y));
}
}

inline void Input ()
{
N = read(), M = read();
for (int i = 1; i <= M; ++i) E[i].x = read(), E[i].y = read(), E[i].z = read();
}

int main()
{
#ifndef ONLINE_JUDGE
freopen("truck.in", "r", stdin);
freopen("truck.out", "w", stdout);
#endif
Input();
Solve();
return 0;
}