【YBTOJ】【Luogu P1967】[NOIP2013 提高组] 货车运输
链接:
题目大意:
给定一个图,每次问两点,求两点间最小值最大是多少。
正文:
两点间不只有一条路径,我们要求“最大”,实际上是将它化为只有一条路径,即最大生成树。
接着就是树剖板子题,但也可以用 LCA 做。
代码:
const int N = 1e4 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, m, Log;
int head[N], tot;
struct node
{
int from, to, val, nxt;
bool operator < (const node &a) const
{
return val > a.val;
}
}e[10 * N], inp[10 * N];
void add(int u, int v, int w)
{
e[++tot] = (node){u, v, w, head[u]}, head[u] = tot;
}
int fa[N];
int Find(int k) {return k == fa[k]? k: fa[k] = Find(fa[k]);}
int dep[N], f[N][27], g[N][27];
void dfs(int u, int d)
{
dep[u] = d;
for (int v, i = head[u]; i; i = e[i].nxt)
{
if (dep[v = e[i].to]) continue;
g[v][0] = e[i].val, f[v][0] = u;
for (int j = 1; j <= Log; j++)
f[v][j] = f[f[v][j - 1]][j - 1],
g[v][j] = min(g[v][j - 1], g[f[v][j - 1]][j - 1]);
dfs(v, d + 1);
}
}
int LCA(int u, int v)
{
if (Find(u) != Find(v)) return -1;
if (dep[u] > dep[v]) u ^= v ^= u ^= v;
int ans = 707406378;
for (int j = Log; ~j; j--)
if (dep[f[v][j]] >= dep[u])
ans = min(ans, g[v][j]),
v = f[v][j];
if (u == v) return ans;
for (int j = Log; ~j; j--)
if (f[u][j] != f[v][j])
ans = min(ans, min(g[v][j], g[u][j])),
u = f[u][j], v = f[v][j];
return min(ans, min(g[u][0], g[v][0]));
}
int main()
{
memset (g, 127 / 3, sizeof g);
n = Read(), m = Read();
Log = log2(n) + 1;
for (int i = 1; i <= m; i++)
{
int u = Read(), v = Read(), w = Read();
inp[i] = (node){u, v, w, 0};
}
sort (inp + 1, inp + 1 + m);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++)
{
int u = inp[i].from, v = inp[i].to, w = inp[i].val,
U = Find(u), V = Find(v);
if (U == V) continue;
fa[U] = V;
add(u, v, w), add(v, u, w);
}
for (int i = 1; i <= n; i++)
if (!dep[i]) dfs(i, 1);
for (int t = Read(); t--; )
{
int a = Read(), b = Read();
printf ("%d\n", LCA(a, b));
}
return 0;
}