1 条题解
-
0
自动搬运
来自洛谷,原作者为

Invisible_H
**搬运于
2025-08-24 23:16:34,当前版本为作者最后更新于2025-05-22 16:32:53,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Part 1
前置知识:最小生成树。
先进行一个转化:
每次花费 的代价,可以连 这一条边。
然后我们需要求 的最小生成树。
代码很好写啊就建完全图跑最小生成树,记得清空 数组,时间复杂度 。
#include <bits/stdc++.h> #define int long long constexpr int maxn = 1e6 + 7; int B[maxn]; struct node{ int u, v, w; }G[maxn]; int fa[maxn], n, q; inline int find(int u){ if(u != fa[u]){ fa[u] = find(fa[u]); } return fa[u]; } inline bool cmp(node a, node b){ return a.w < b.w; } int32_t main(){ std::cin.tie(nullptr) -> std::ios::sync_with_stdio(false); std::cin >> n >> q; for(int i = 1; i <= n; i++){ std::cin >> B[i]; } for(int i = 0; i <= n; i++){ fa[i] = i; } int m = 0; int k, val; for(int i = 0; i <= n; i++){ int g = B[i + 1]; for(int j = i + 1; j <= n; j++){ g = std::__gcd(g, B[j]); G[++m].u = i; G[m].v = j; G[m].w = g; } } int ans = 0; std::sort(G + 1, G + m + 1, cmp); int cnt = 0; for(int i = 1; i <= m; i++){ int fu = find(G[i].u), fv = find(G[i].v); if(fu == fv) continue; ans += G[i].w; fa[fv] = fu; cnt++; if(cnt == n) break; } std::cout << ans << '\n'; while(q--){ for(int i = 0; i <= n; i++){ fa[i] = i; } int m = 0; int k, val; std::cin >> k >> val; B[k] = val; for(int i = 0; i <= n; i++){ int g = B[i + 1]; for(int j = i + 1; j <= n; j++){ g = std::__gcd(g, B[j]); G[++m].u = i; G[m].v = j; G[m].w = g; } } int ans = 0; std::sort(G + 1, G + m + 1, cmp); int cnt = 0; for(int i = 1; i <= m; i++){ int fu = find(G[i].u), fv = find(G[i].v); if(fu == fv) continue; ans += G[i].w; fa[fv] = fu; cnt++; if(cnt == n) break; } std::cout << ans << '\n'; } return 0; }Part 2
考虑优化。
前置知识:最小生成树。
根据
Kruskal的思想, 这条边一定会被选。然后根据
Prim的思想,对于某个点,我们需要找到其最短的出边。而显然对于 ,它最短的出边为 或者 。边权为 和 。
然后你只需要建 到所有点的边和 到所有点的边就好了,时间复杂度 。
#include <bits/stdc++.h> #define int long long constexpr int maxn = 1e6 + 7; int B[maxn], m; struct node{ int u, v, w; }G[maxn]; int fa[maxn], n, q; inline int find(int u){ if(u != fa[u]){ fa[u] = find(fa[u]); } return fa[u]; } inline bool cmp(node a, node b){ return a.w < b.w; } int32_t main(){ std::cin.tie(nullptr) -> std::ios::sync_with_stdio(false); std::cin >> n >> q; for(int i = 1; i <= n; i++){ std::cin >> B[i]; } for(int i = 0; i <= n; i++){ fa[i] = i; } int m = 0; int k, val; int g = 0; for(int i = 1; i <= n; i++){ g = std::__gcd(g ,B[i]); G[++m].u = 0; G[m].v = i; G[m].w = g; } g = 0; for(int i = n; i >= 1; i--){ g = std::__gcd(g ,B[i]); G[++m].u = i - 1; G[m].v = n; G[m].w = g; } int ans = 0; std::sort(G + 1, G + m + 1, cmp); int cnt = 0; for(int i = 1; i <= m; i++){ int fu = find(G[i].u), fv = find(G[i].v); if(fu == fv) continue; ans += G[i].w; fa[fv] = fu; cnt++; if(cnt == n) break; } std::cout << ans << '\n'; while(q--){ for(int i = 0; i <= n; i++){ fa[i] = i; } int m = 0; int k, val; std::cin >> k >> val; B[k] = val; int g = 0; for(int i = 1; i <= n; i++){ g = std::__gcd(g ,B[i]); G[++m].u = 0; G[m].v = i; G[m].w = g; } g = 0; for(int i = n; i >= 1; i--){ g = std::__gcd(g ,B[i]); G[++m].u = i - 1; G[m].v = n; G[m].w = g; } int ans = 0; std::sort(G + 1, G + m + 1, cmp); int cnt = 0; for(int i = 1; i <= m; i++){ int fu = find(G[i].u), fv = find(G[i].v); if(fu == fv) continue; ans += G[i].w; fa[fv] = fu; cnt++; if(cnt == n) break; } std::cout << ans << '\n'; } return 0; }Part 3
考虑继续优化。
前置知识:线段树二分。
显然 是单调不增, 是单调不减的。
所以一定存在一个 $p \in [0,n),\forall i\in[0,p],R_i \leq Li,\forall i \in (p,n),Li \leq Ri$。
我们可以用线段树维护每个区间 的 ,然后在线段树上二分求出 。
而题目所给的修改可以直接单点修改。
剩下的就是求 。
考虑到 以及 的取值个数是 级别的,我们可以在线段树上暴力找出这些取值以及其对应的区间,时间复杂度 。
#include <bits/stdc++.h> #define mid ((l+r)>>1) #define int long long int SGT[400007]; void build(int p, int l, int r) { if (l == r) return (void)(std::cin >> SGT[p]); build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r), SGT[p] = std::__gcd(SGT[p << 1], SGT[p << 1 | 1]); } void update(int p, int l, int r, int x, int v) { if (l == r) return (void)(SGT[p] = v); x <= mid ? update(p << 1, l, mid, x, v) : update(p << 1 | 1, mid + 1, r, x, v); SGT[p] = std::__gcd(SGT[p << 1], SGT[p << 1 | 1]); } int Find(int p, int l, int r, int a, int b) { if (l == r) return l; int x = std::__gcd(a, SGT[p << 1]), y = std::__gcd(b, SGT[p << 1 | 1]); return x <= y ? Find(p << 1, l, mid, a, y) : Find(p << 1 | 1, mid + 1, r, x, b); } int cal1(int p, int l, int r, int x, int v) { if (l == r) return std::__gcd(SGT[p], v); int a = std::__gcd(SGT[p << 1 | 1], v), b = std::__gcd(SGT[p << 1], a); return x <= mid ? cal1(p << 1, l, mid, x, a) : (a == b ? 1ll * (mid - l + 1) * a : cal1(p << 1, l, mid, x, a)) + cal1(p << 1 | 1, mid + 1, r, x, v); } int cal2(int p, int l, int r, int x, int v) { if (l == r) return std::__gcd(SGT[p], v); int a = std::__gcd(SGT[p << 1], v), b = std::__gcd(SGT[p << 1 | 1], a); return x > mid ? cal2(p << 1 | 1, mid + 1, r, x, a) : (a == b ? 1ll * (r - mid) * a : cal2(p << 1 | 1, mid + 1, r, x, a)) + cal2(p << 1, l, mid, x, v); } int32_t main() { int n, Q; std::cin >> n >> Q; build(1, 1, n); int p = Find(1, 1, n, 0, 0); std::cout << (cal1(1, 1, n, p, 0) + cal2(1, 1, n, p, 0) - SGT[1]) << '\n'; for (int p, v; Q; --Q) std::cin >> p >> v, update(1, 1, n, p, v), p = Find(1, 1, n, 0, 0), std::cout << (cal1(1, 1, n, p, 0) + cal2(1, 1, n, p, 0) - SGT[1]) << '\n'; return 0; }
- 1
信息
- ID
- 12350
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者