[luogu1967] 货车运输
题面
这个题目第一眼看觉得是道最短路,但是一看数据范围,有3e5次询问,1e5个点,所以最终用上dijkstra的时间复杂度便是:
\[O(qn ^ 2)
\]
加上堆优化之后也只有:
\[O(qn\log_{2}^{n})
\]
所以,我们肯定只能用别的方法来做了。
继续看题,题目中如是说道:"司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。", 又这样说道:"x 不等于 y,两座城市之间可能有多条道路 。" 这句话是很重要的,它说明了若是x, y之间有多余的边,那么只取其中最长的那条边就是了, 所以这就是最大生成树了, 因为两两之间只有一条边。
等会, 好像还是有点问题
若两个点之间不联通呢??? 那我们就不建树, 建一棵最大生成森林就可以了, 如果询问的两个点不在同一棵树中的话,就输出不成立。至于查询两点间的距离的话, 不是LCA的模版题吗, 这道题就被我们完美(莫名其妙)地解决了。所以,一个题只要剖析清楚,其实就没有那么难了。
代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define N 100005
#define M 500005
using namespace std;
int n, m, head[N], cnt, fa[N], tot, rnk[N], f[N][25], dis[N][25], dep[N], q;
struct node
{
int to, from, cost, next;
} edge[M << 1], e[N << 1];
inline int read()
{
int x = 0, w = 1;
char c = getchar();
while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * w;
}
inline void add(int u, int v, int w) { edge[++cnt].to = v; edge[cnt].from = u; edge[cnt].next = head[u]; edge[cnt].cost = w; head[u] = cnt; }
inline void adde(int u, int v, int w) { e[++tot].to = v; e[tot].from = u; e[tot].cost = w; e[tot].next = head[u]; head[u] = tot; }
inline bool cmp(node a, node b) { return a.cost > b.cost; }
inline int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
inline void merge(int x, int y)
{
x = find(x); y = find(y);
if(rnk[x] < rnk[y]) fa[x] = y;
else if(rnk[x] > rnk[y]) fa[y] = x;
else { fa[x] = y; rnk[y]++; }
}
inline void Kruskal()
{
memset(head, 0, sizeof(head));
for(int i = 1; i <= n; i++) { fa[i] = i; rnk[i] = 1; }
sort(edge + 1, edge + cnt + 1, cmp);
for(int i = 1; i <= cnt; i++)
{
int u = edge[i].from, v = edge[i].to;
int r1 = find(u), r2 = find(v);
if(r1 != r2)
{
merge(r1, r2);
adde(u, v, edge[i].cost); adde(v, u, edge[i].cost);
}
}
}
inline void dfs(int u, int fa)
{
f[u][0] = fa; dep[u] = dep[fa] + 1;
for(int i = 1; i <= 20 && f[f[u][i - 1]][i - 1]; i++)
{
f[u][i] = f[f[u][i - 1]][i - 1];
dis[u][i] = min(dis[u][i - 1], dis[f[u][i - 1]][i - 1]);
}
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to; if(v == fa) continue;
dis[v][0] = e[i].cost; dfs(v, u);
}
}
inline int lca(int u, int v)
{
if(dep[u] > dep[v]) swap(u, v);
int ans = 1e9;
for(int i = 18; i >= 0; i--)
if(dep[f[v][i]] >= dep[u])
{
ans = min(ans, dis[v][i]);
v = f[v][i];
}
if(u == v) return ans;
for(int i = 20; i >= 0; i--)
if(f[u][i] != f[v][i])
{
ans = min(ans, min(dis[v][i], dis[u][i]));
v = f[v][i]; u = f[u][i];
}
ans = min(ans, min(dis[v][0], dis[u][0]));
return ans;
}
int main()
{
memset(f, 0, sizeof(f));
n = read(); m = read();
for(int i = 1; i <= m; i++)
{
int u = read(), v = read(), w = read();
add(u, v, w);
}
Kruskal();
for(int i = 1; i <= n; i++)
if(!dep[i])
dfs(i, 0);
q = read();
for(int i = 1; i <= q; i++)
{
int x = read(), y = read();
if(find(x) != find(y)) { puts("-1"); continue; }
printf("%d\n", lca(x, y));
}
return 0;
}