倍增
Minimum spanning tree for each edge
求出一颗最小生成树, 然后由于我们必须包括第 i 条边
假设这条边在最小生成树上就是最小生成树的权值和
反之, 我们需要加入这条边到最小生成树上, 我们假设这条边连接 u, v 两条边, 那么我们贪心的去拿最小生成树上最长边断开, 加入该边
最小生成树上两点路径最大值可以在求 lca 时顺便倍增维护

#include <bits/stdc++.h> using namespace std; #define endl "\n"; #define int long long typedef long long ll; const int M = 2e5 + 100; const int N = 2e5 + 100; struct Node { int x, y, v, id; } edge[M]; int n, m, fa[N], vis[N]; vector<array<int, 2>> g[N]; int findset(int i) { if (i == fa[i]) return i; return fa[i] = findset(fa[i]); } bool cmp(Node a, Node b) { return a.v < b.v; } ll Kruskal() { for (int i = 1; i <= n; i++) fa[i] = i; sort(edge + 1, edge + 1 + m, cmp); ll ans = 0, cnt = n; for (int i = 1; i <= m; i++) { auto [x, y, v, id] = edge[i]; int fx = findset(x); int fy = findset(y); if (fx != fy) { fa[fx] = fy; ans += v; g[x].push_back({y, v}); g[y].push_back({x, v}); vis[id] = 1; cnt--; } if (cnt == 1) break; } return ans; } int father[N][25], f[N][25], dist[N]; ll res[N]; void dfs(int x, int fa) { for (auto [y, v] : g[x]) { if (y == fa) continue; dist[y] = dist[x] + 1; father[y][0] = x; f[y][0] = v; dfs(y, x); } } int query(int x, int y) { if (dist[x] < dist[y]) swap(x, y); int z = dist[x] - dist[y]; int ans = 0; for (int i = 0; i <= 20 && z; i++, z >>= 1) { if (z & 1) ans = max(ans, f[x][i]), x = father[x][i]; } if (x == y) return ans; for (int i = 20; i >= 0; i--) { if (father[x][i] != father[y][i]) ans = max({f[x][i], f[y][i], ans}), x = father[x][i], y = father[y][i]; } ans = max(ans, f[x][0]); ans = max(ans, f[y][0]); return ans; } void solve() { cin >> n >> m; for (int i = 1; i <= m; i++) { int x, y, v; cin >> x >> y >> v; edge[i] = {x, y, v, i}; } ll sum = Kruskal(); dist[1] = 1; dfs(1, 1); for (int i = 1; i <= 20; i++) { for (int j = 1; j <= n; j++) { f[j][i] = max(f[j][i - 1], f[father[j][i - 1]][i - 1]); father[j][i] = father[father[j][i - 1]][i - 1]; } } for (int i = 1; i <= m; i++) { auto [x, y, v, id] = edge[i]; if (vis[id]) res[id] = sum; else res[id] = sum + v - query(x, y); } for (int i = 1; i <= m; i++) cout << res[i] << endl; } signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); // int T; cin >> T; // while (T--) solve(); solve(); return 0; }
和上题差不多
Analysis of Pathes in Functional Graph
和上面两题都差不多,只是维护东西稍微不同

#include <bits/stdc++.h> using namespace std; #define endl "\n" #define int long long typedef long long ll; const int N = 1e5 + 100; int f[N][40], g[N][40], v[N][40]; void query(int x, int k) { int ans = LLONG_MAX, res = 0; for (int i = 0; i <= 40 && k; i++, k >>= 1) { if (k & 1) { ans = min(v[x][i], ans); res = res + g[x][i]; x = f[x][i]; } } cout << res << ' ' << ans << endl; } void solve() { int n, k; cin >> n >> k; memset(v, 127, sizeof(v)); for (int i = 1; i <= n; i++) cin >> f[i][0], f[i][0]++; for (int i = 1; i <= n; i++) cin >> g[i][0], v[i][0] = g[i][0]; for (int i = 1; i <= 40 && 1ll << i <= k; i++) { for (int j = 1; j <= n; j++) { if (f[j][i - 1]) { f[j][i] = f[f[j][i - 1]][i - 1]; v[j][i] = min(v[j][i - 1], v[f[j][i - 1]][i - 1]); g[j][i] = g[j][i - 1] + g[f[j][i - 1]][i - 1]; } } } for (int i = 1; i <= n; i++) query(i, k); } signed main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); // int T = 1; cin >> T; // while(T--) solve(); solve(); return 0; }
很有意思的一题
首先我们知道倍增是维护一个东西走 2 ^ i 步的状态
对于这题 我们知道划分出来每一段的数质因子都只能出现一次, 我们根据这个性质贪心, 就可以得到每划一次能到的最远点
这时我们就可以把区间 [ L , R ] 的查询转换成从 L 走 x 次到 R, 倍增去跳就行
这里 f [ i ][ j ] 维护的是 i 跳 2 ^ j 步到的点的下一个点

#include <bits/stdc++.h> using namespace std; #define endl "\n"; typedef long long ll; const int N = 1e5 + 100; int a[N], f[N][25], pos[N]; vector<int> Div[N]; bool primes[N]; void init() { for (int i = 2; i <= N - 100; i++) { if (!primes[i]) { Div[i].push_back(i); for (int j = i + i; j <= N - 100; j += i) { primes[j] = true; Div[j].push_back(i); } } } } int query(int l, int r) { int ans = 0; for (int i = 20; i >= 0; i--) { if (f[l][i] <= r) { ans += (1 << i); l = f[l][i]; } } return ans + 1; } void solve() { int n, q; cin >> n >> q; for (int i = 1; i <= n; i++) cin >> a[i]; f[n + 1][0] = n + 1; memset(pos, 127, sizeof(pos)); for (int i = n; i >= 1; i--) { f[i][0] = f[i + 1][0]; for (auto j : Div[a[i]]) { f[i][0] = min(pos[j], f[i][0]); pos[j] = i; } } for (int i = 1; i <= 20; i++) { for (int j = 1; j <= n; j++) { if (f[j][i - 1] <= n) f[j][i] = f[f[j][i - 1]][i - 1]; else f[j][i] = n + 1; } } while (q--) { int l, r; cin >> l >> r; cout << query(l, r) << endl; } } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); init(); // int T; cin >> T; // while (T--) solve(); solve(); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】