Kruskal 重构树学习笔记
模拟赛突然出了这个,,,被创死了
定义
我们先回顾最基本的 Kruskal 算法。
Kruskal 算法是一种常见并且好写的最小生成树算法,由 Kruskal 发明。该算法的基本思想是从小到大加入边,是个贪心算法。
那什么事 Kruskal 重构树呢?
就是用类似Kruskal的方法来把一个图重构成一颗树。
实现方法
按照最小生成树的方法先把边权从小到大排序,然后一次考虑每条边。
如果这条边连接的两个端点已经连通就跳过,还没连通就建一个新点,把这个新点的权值设为这条边的权值,让这个新点向两个端点的根节点连边。
具体实现方法如下:
tot = n;
dsu.init();
for(int i = 1; i <= m; i ++)
cin >> edge[i][1] >> edge[i][2] >> edge[i][0];
sort(edge + 1, edge + 1 + m);
for(int i = 1; i <= m; i ++) {
int u = dsu.find(edge[i][1]), v = dsu.find(edge[i][2]);
if(u == v) continue;
tot ++;
dsu.fa[u] = dsu.fa[v] = tot;
e[tot].push_back(u);
e[tot].push_back(v);
val[tot] = edge[i][0];
}
优秀性质:
-
是一棵二叉树。
-
如果是按最小生成树建立的话是一个大根堆。
-
原图中两个点间所有路径上的边最大权值的最小值
最小生成树上两点简单路径的边最大权值 重构树上两点 的点权。
例题#
1. P2245 星际导航#
显然是板子题,直接利用性质 3,会写 LCA 就行。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 2e5 + 5, M = 3e5 + 5, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, m, q;
array<int, 3> edge[M];
int p[N];
int fa[N][20], val[N], dep[N];
vector<int> e[N];
int tot;
int find(int x) {
return p[x] == x ? x : p[x] = find(p[x]);
}
void dfs(int u, int fath) {
dep[u] = dep[fath] + 1;
fa[u][0] = fath;
for(int i = 1; i <= __lg(dep[u]); i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(auto v : e[u]) dfs(v, u);
}
int lca(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
while(dep[u] != dep[v]) u = fa[u][__lg(dep[u] - dep[v])];
if(u == v) return u;
for(int k = __lg(dep[u]); ~k; k --)
if(fa[u][k] != fa[v][k])
u = fa[u][k], v = fa[v][k];
return fa[u][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
for(int i = 1; i <= n * 2; i ++) p[i] = i;
for(int i = 1; i <= m; i ++)
cin >> edge[i][1] >> edge[i][2] >> edge[i][0];
tot = n;
sort(edge + 1, edge + 1 + m);
for(int i = 1; i <= m; i ++) {
int u = find(edge[i][1]), v = find(edge[i][2]);
if(u == v) continue;
tot ++;
p[u] = p[v] = tot;
e[tot].push_back(u);
e[tot].push_back(v);
val[tot] = edge[i][0];
}
for(int i = 1; i <= tot; i ++)
if(find(i) == i) dfs(i, 0);
cin >> q;
while(q --) {
int u, v;
cin >> u >> v;
if(find(u) != find(v)) cout << "impossible\n";
else cout << val[lca(u, v)] << '\n';
}
return 0;
}
2.囹圄#
同学出的题,就不放链接了。
题意:
在一个神奇的国家有
同时,定义一个地点的广阔度为其能到达的地方的局限值中没有出现过的最小正整数。
最近这个国家打算修路,有
1 x y
表示修一条连接
2 x
表示查询
对于
分析:
有一个显然的
最优解显然可以线段树合并,但是我没学。
考虑按询问顺序建重构树,对于每次询问,倍增寻找能到的节点,把叶子节点建立权值线段树。
然后就可以 P4137了。
3. 黄队#
模拟赛题,链接放了也没人进得去(
有一棵
现在有
由于答案很大,请输出对
对于
分析:
杜老师几年前出的题,性质还是很好找的,找到性质就 80 pts 了(
考虑
1 个点。
所以
到这里之后暴力就有 80 了,就是我场上拿的分。(
考虑建出重构树,每次询问按
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 4e5 + 5, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, q, tot;
int p[N], val[N], fa[N][20], dep[N], dfn[N], tim;
int find(int x) {
return p[x] == x ? x : p[x] = find(p[x]);
}
vector<int> e[N];
void dfs(int u, int fath) {
fa[u][0] = fath;
dfn[u] = ++ tim;
dep[u] = dep[fath] + 1;
for(int i = 1; i <= __lg(dep[u]); i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(auto v : e[u]) dfs(v, u);
}
int lca(int u, int v) {
if(dep[u] < dep[v]) swap(u, v);
while(dep[u] != dep[v]) u = fa[u][__lg(dep[u] - dep[v])];
if(u == v) return u;
for(int k = __lg(dep[u]); ~k; k --)
if(fa[u][k] != fa[v][k])
u = fa[u][k], v = fa[v][k];
return fa[u][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n;
tot = n;
for(int i = 1; i <= n * 2; i ++) p[i] = i;
for(int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
u = find(u), v = find(v);
tot ++;
p[u] = p[v] = tot;
e[tot].push_back(u);
e[tot].push_back(v);
val[tot] = i;
}
cin >> q;
dfs(tot, 0);
while(q --) {
int k;
int ans = 1;
cin >> k;
vector<int> a(k);
for(int i = 0; i < k; i ++)
cin >> a[i];
sort(a.begin(), a.end(), [](int x, int y) {
return dfn[x] < dfn[y];
});
for(int i = 0; i < k; i ++) {
int tmp = n - 1;
if(i) tmp = val[lca(a[i], a[i - 1])] - 1;
if(i < k - 1) tmp = min(tmp, val[lca(a[i], a[i + 1])] - 1);
ans = (LL)(tmp + 1ll) * ans % mod;
}
cout << ans << '\n';
}
return 0;
}
4.P7834 [ONTAK2010] Peaks 加强版#
求从
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 2e5 + 5, M = 5e5 + 5, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, m, q;
int h[N];
vector<int> hs;
array<int, 3> edge[M];
vector<int> e[N];
int val[N], fa[N][25], p[N], L[N], R[N], dep[N];
struct DSU {
int fa[N];
void init() {
for(int i = 1; i <= n * 2; i ++) fa[i] = i;
}
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
} dsu;
int tot, idx;
void dfs(int u, int fath) {
L[u] = idx;
if(u <= n) p[++ idx] = u;
dep[u] = dep[fath] + 1;
fa[u][0] = fath;
for(int i = 1; i <= __lg(dep[u]); i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(auto v : e[u]) dfs(v, u);
R[u] = idx;
}
struct SegT {
int l, r, val;
#define l(x) tr[x].l
#define r(x) tr[x].r
#define val(x) tr[x].val
} tr[N << 5];
int rt[N], cnt;
void pushup(int x) {
val(x) = val(l(x)) + val(r(x));
}
void build(int &x, int l, int r) {
x = ++ cnt;
if(l == r) return;
int mid = l + r >> 1;
build(l(x), l, mid), build(r(x), mid + 1, r);
}
void insert(int &x, int lst, int pos, int l, int r) {
x = ++ cnt;
tr[x] = tr[lst];
if(l == r) {
val(x) ++;
return;
}
int mid = l + r >> 1;
if(pos <= mid) insert(l(x), l(lst), pos, l, mid);
else insert(r(x), r(lst), pos, mid + 1, r);
pushup(x);
}
int query(int x, int y, int l, int r, int k) {
if(l == r) return l;
int cnt = val(r(y)) - val(r(x)), mid = l + r >> 1;
if(k <= cnt) return query(r(x), r(y), mid + 1, r, k);
else return query(l(x), l(y), l, mid, k - cnt);
}
int get(int u, int x) {
for(int i = 19; ~i; i --)
if(val[fa[u][i]] <= x) u = fa[u][i];
return u;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> q;
for(int i = 1; i <= n; i ++) {
cin >> h[i];
hs.push_back(h[i]);
}
sort(hs.begin(), hs.end());
hs.erase(unique(hs.begin(), hs.end()), hs.end());
for(int i = 1; i <= n; i ++) {
h[i] = lower_bound(hs.begin(), hs.end(), h[i]) - hs.begin() + 1;
}
tot = n;
dsu.init();
for(int i = 1; i <= m; i ++)
cin >> edge[i][1] >> edge[i][2] >> edge[i][0];
sort(edge + 1, edge + 1 + m);
for(int i = 1; i <= m; i ++) {
int u = dsu.find(edge[i][1]), v = dsu.find(edge[i][2]);
if(u == v) continue;
tot ++;
dsu.fa[u] = dsu.fa[v] = tot;
e[tot].push_back(u);
e[tot].push_back(v);
val[tot] = edge[i][0];
}
val[0] = LLONG_MAX;
for(int i = tot; i >= 1; i --)
if(dsu.fa[i] == i) dfs(i, 0);
build(rt[0], 1, hs.size());
for(int i = 1; i <= n; i ++) {
insert(rt[i], rt[i - 1], h[p[i]], 1, hs.size());
}
int lst = 0;
while(q --) {
int v, x, k;
cin >> v >> x >> k;
v = (v ^ lst) % n + 1;
k = (k ^ lst) % n + 1;
x ^= lst;
v = get(v, x);
if(R[v] - L[v] < k) cout << -1 << '\n', lst = 0;
else {
lst = hs[query(rt[L[v]], rt[R[v]], 1, hs.size(), k) - 1];
cout << lst << '\n';
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!