文章目录
无向图, N N N个节点,表示询问从 A A A点走到 B B B点的所有路径中,最长的边最小值是多少?
K r u s k a l Kruskal Kruskal 重构树 or 树链剖分 or 树上倍增
分析
对于此题分析:
在所有路径中取最长的那一条边,使得边最小
假设找到了这条路径
那么这条路径里面的其它边必然小于等于这条边,且这时候
A
A
A 和
B
B
B 是联通的,也就是说,其它能够联通
A
A
A 和
B
B
B 的路径中,一定会存在大于等于这个值的边。
假如我们在图中删除所有大于等于这个值的边,
A
A
A 和
B
B
B 就一定不连通了。因此,我们需要找到恰好能够联通
A
A
A 和
B
B
B 的边长度就行了。
这不就是最小生成树吗?
从最小的边开始加,一旦发现
A
A
A 和
B
B
B 联通了,联通
A
A
A 和
B
B
B的那条路径里面的最大边就是所求!
我们能够在执行 K r u s k a l Kruskal Kruskal 的过程中,构造一棵最小生成树。用树链剖分,或者树上倍增维护链上的边最大值
关于 K r u s k a l Kruskal Kruskal 重构树,同样在执行 K r u s k a l Kruskal Kruskal的时候,如果要将两个点用并查集合并的时候,建立一个新节点,此新节点分别向这两个点( f i n d find find后的值,也就是连通块的祖先)连一条有向边,新节点的点权为新加入边的边权,当算法执行完后,就完成了 K r u s k a l Kruskal Kruskal重构树的构建
重构树的性质(最 小/大 生成树的情况下)
- 某重构树中的节点,的子树都能在通过 小/大 于等于此节点点权的情况下到达
- L C A ( u , v ) LCA(u,v) LCA(u,v)的权值为 u u u到 v v v所有路径中最 长/小 边最 小/长 的值
- 重构树为一个 大/小 顶堆
此题的情况下,构建一颗重构树,直接在重构树中找LCA即可
t i p s : tips: tips:因为所有的原图点都是通过新建点相连的,所以任意两个原图点的 l c a lca lca必然是新建点,不同担心找错了 l c a lca lca,从而得到错误点权
代码
Kruskal重构树
//bz3732
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;
int head[MAX_N];
int tot = 0;
struct Edge{
int to, nxt;
}edge[MAX_N];
void addEdge(int u, int v) {
edge[tot].nxt = head[u];
edge[tot].to = v;
head[u] = tot++;
}
struct Node {
int u, v, w;
bool operator < (const Node& B) const {
return w < B.w;
}
}arr[MAX_N];
int parent[MAX_N];
int find(int x) {
return x==parent[x]?x:parent[x]=find(parent[x]);
}
int father[MAX_N][21];
int dep[MAX_N];
void dfs(int u, int from, int d) {
father[u][0] = from;
dep[u] = d;
for (int i = 1; i <= 20; ++i) {
father[u][i] = father[father[u][i-1]][i-1];
}
int v;
for (int i = head[u];~i;i=edge[i].nxt) {
dfs(v=edge[i].to, u, d+1);
}
}
int LCA(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
for (int i = 20; i+1; --i) {
if (dep[father[y][i]] >= dep[x]) {
y = father[y][i];
}
}
if (x == y) return x;
for (int i = 20; i+1; --i) {
if (father[x][i] != father[y][i]) {
x = father[x][i];
y = father[y][i];
}
}
return father[x][0];
}
int brr[MAX_N];
int indx = 0;
void init() {
memset(head, -1, sizeof head);
tot = 0;
}
void solve(){
sc("%lld%lld%lld", &N, &M, &K);
init();
for (int i = 1; i <= 2*N; ++i) {
parent[i] = i;
}
for (int i = 1; i <= M; ++i) {
sc("%lld%lld%lld", &arr[i].u, &arr[i].v, &arr[i].w);
}
sort(arr+1, arr+1+M);
int x, y;
indx = N;
for (int i = 1; i <= M; ++i) {
x = find(arr[i].u);
y = find(arr[i].v);
if (x != y) {
parent[x] = parent[y] = ++indx;
brr[indx] = arr[i].w;
addEdge(indx, x);
addEdge(indx, y);
}
}
dfs(indx, 0, 1);
for (int i = 1; i <= K; ++i) {
sc("%lld%lld", &x, &y);
pr("%lld\n", brr[LCA(x, y)]);
}
}
signed main()
{
#ifndef ONLINE_JUDGE
//FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}
树上倍增
树链剖分同样思路,维护区间最值
//bz3732
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;
int head[MAX_N];
int tot = 0;
struct Edge{
int to, nxt, w;
}edge[MAX_N];
void addEdge(int u, int v, int w) {
edge[tot].nxt = head[u];
edge[tot].to = v;
edge[tot].w = w;
head[u] = tot++;
edge[tot].nxt = head[v];
edge[tot].to = u;
edge[tot].w = w;
head[v] = tot++;
}
struct Node {
int u, v, w;
bool operator < (const Node& B) const {
return w < B.w;
}
}arr[MAX_N];
int parent[MAX_N];
int find(int x) {
return x==parent[x]?x:parent[x]=find(parent[x]);
}
int father[MAX_N][21];
int maxx[MAX_N][21];
int dep[MAX_N];
void dfs(int u, int from, int d) {
father[u][0] = from;
dep[u] = d;
for (int i = 1; i <= 20; ++i) {
father[u][i] = father[father[u][i-1]][i-1];
maxx[u][i] = max(maxx[u][i-1], maxx[father[u][i-1]][i-1]);
}
int v;
for (int i = head[u];~i;i=edge[i].nxt) {
if ((v=edge[i].to) == from) continue;
maxx[v][0] = edge[i].w;
dfs(v, u, d+1);
}
}
int query(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
int ans = 0;
for (int i = 20; i+1; --i) {
if (dep[father[y][i]] >= dep[x]) {
ans = max(ans, maxx[y][i]);
y = father[y][i];
}
}
for (int i = 20; i+1; --i) {
if (father[x][i] != father[y][i]) {
ans = max(ans, max(maxx[x][i], maxx[y][i]));
x = father[x][i];
y = father[y][i];
}
}
if (x != y) {
ans = max(ans, max(maxx[x][0], maxx[y][0]));
}
return ans;
}
void init() {
memset(head, -1, sizeof head);
tot = 0;
}
void solve(){
sc("%lld%lld%lld", &N, &M, &K);
init();
for (int i = 1; i <= N; ++i) {
parent[i] = i;
}
for (int i = 1; i <= M; ++i) {
sc("%lld%lld%lld", &arr[i].u, &arr[i].v, &arr[i].w);
}
sort(arr+1, arr+1+M);
int x, y;
for (int i = 1; i <= M; ++i) {
x = find(arr[i].u);
y = find(arr[i].v);
if (x != y) {
parent[x] = y;
addEdge(arr[i].u, arr[i].v, arr[i].w);
}
}
dfs(1, 0, 1);
for (int i = 1; i <= K; ++i) {
sc("%lld%lld", &x, &y);
pr("%lld\n", query(x, y));
}
}
signed main()
{
#ifndef ONLINE_JUDGE
//FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}