SMU winter 2025 Personal Round 3
SMU winter 2025 Personal Round 3
A. Vasya and Book
思路
计算从起始页 \(x\) 到目标页 \(y\) 的最小按键次数。每次按键可向前或向后滚动 \(d\) 页,但不可越界。解题关键在于分析三种可能路径:
- 直接移动:若 \(|x - y|\) 能被 \(d\) 整除,则直接移动,次数为 \(\frac{|x - y|}{d}\)。
- 经第一页移动:若 \(y\) 到第一页的步数 \((y-1)\) 能被 \(d\) 整除,则总次数为从 \(x\) 到第一页的步数加上从第一页到 \(y\) 的步数。
- 经最后一页移动:若最后一页到 \(y\) 的步数 \((n - y)\) 能被 \(d\) 整除,则总次数为从 \(x\) 到最后一页的步数加上从最后一页到 \(y\) 的步数。
取上述三种情况的最小值即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n, d, x, y; cin >> n >> x >> y >> d; int ans = 1 << 30; if (abs(x - y) % d == 0) { ans = min(ans, abs(x - y) / d); } if ((y - 1) % d == 0) { ans = min(ans, (y - 1) / d + (x + d - 1) / d); } if ((n - y) % d == 0) { ans = min(ans, (n - y) / d + (n - x + d - 1) / d); } if (ans == 1 << 30) { ans = -1; } cout << ans << "\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
B. Vova and Trophies
思路
预处理前缀数组 \(\text{pre}\) 和后缀数组 \(\text{suf}\),分别表示以位置 \(i\) 结尾和开头的最长连续 'G' 的长度。遍历每个位置,若当前位置为 'S',则其左右两侧的连续 'G' 可合并为 \(\text{pre}[i-1] + \text{suf}[i+1]\)。若总 'G' 数 \(\text{cnt}\) 大于该值,则存在可交换的 'G',总长度可再加 \(1\)。最终取所有情况的最大值,并处理全为 'G' 的特殊情况即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; string s; cin >> s; s = " " + s; int cnt = 0, ans = 0; vector<int> pre(n + 1), suf(n + 2); for (int i = 1; i <= n; i ++) { cnt += s[i] == 'G'; if (s[i] == 'G') { pre[i] = pre[i - 1] + 1; ans = max(ans, pre[i]); } } for (int i = n; i > 0; i --) { if (s[i] == 'G') { suf[i] = suf[i + 1] + 1; } } for (int i = 1; i <= n; i ++) { if (s[i] != 'G') { ans = max(ans, pre[i - 1] + suf[i + 1] + (cnt - pre[i - 1] - suf[i + 1] > 0)); } } cout << ans << "\n"; return 0; }
C. Multi-Subject Competition
思路
对于每个科目,将学生按技能降序排序,计算前缀和 \(x_j\)(前 \(j\) 项的和)。对于每个可能的 \(k\)(选取人数),累加所有科目中前 \(k\) 项的正前缀和。最终取所有 \(k\) 对应累加和的最大值。具体地,若科目 \(s\) 的前缀和 \(x_j\) 非负,则将其累加至 \(\text{has}[j]\),否则停止。最终答案即 \(\max(\text{has})\),若所有情况均为负则输出 \(0\)。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector g(m, vector<i64>()); for (int i = 0; i < n; i ++) { int s, r; cin >> s >> r; s--; g[s].emplace_back(r); } vector<i64> has(max(n, m)); for (auto &x : g) { if (x.empty()) continue; sort(x.begin(), x.end(), greater<>()); for (int j = 0; j < x.size(); j ++) { if (j) { x[j] += x[j - 1]; } if (x[j] > 0) { has[j] += x[j]; } else { break; } } } cout << *max_element(has.begin(), has.end()) << "\n"; return 0; }
D. Maximum Diameter Graph
思路
首先检查总度数是否满足 \(\sum a_i \geq 2(n-1)\),否则直接输出“NO”。将度数大于 \(1\) 的顶点按降序排列并连接成主干链,其初始直径为 \(k-1\)(\(k\) 为主干顶点数)。优先将度数为 \(1\) 的叶子连接到主干两端,每端最多添加一个叶子,直径最多增加 \(2\),最终直径为 \(k + t - 1\)(\(t\) 为两端实际添加的叶子数,最多 \(2\))。剩余叶子连接到主干中仍有剩余度数的顶点,确保所有度数限制被满足。最终输出最大直径及合法边集。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; int sum = 0; vector<int> a(n), need; vector<pair<int, int>> d; for (int i = 0; i < n; i ++) { cin >> a[i]; sum += a[i]; if (a[i] != 1) { d.emplace_back(a[i], i); } else { need.emplace_back(i); } } if (sum < 2 * (n - 1)) { cout << "NO\n"; return 0; } vector g(n, vector<int>(n)); if (d.size() > 1) { sort(d.begin(), d.end(), greater<>()); sort(d.begin() + 1, d.end()); for (int i = 0; i + 1 < d.size(); i ++) { g[d[i].second][d[i + 1].second] = g[d[i + 1].second][d[i].second] = 1; a[d[i].second]--; a[d[i + 1].second]--; } } int ans = d.size(); if (need.size()) { for (int i : {0, (int)d.size() - 1}) { auto [_, x] = d[i]; if (a[x] && need.size()) { g[x][need.back()] = g[need.back()][x] = 1; a[x]--; ans ++; need.pop_back(); } } for (auto [_, s] : d) { while (a[s] && need.size()) { g[s][need.back()] = g[need.back()][s] = 1; a[s]--; need.pop_back(); } } } cout << "YES " << ans - 1 << "\n"; vector<pair<int, int>> e; for (int i = 0; i < n; i ++) { for (int j = i + 1; j < n; j ++) { if (g[i][j]) { e.emplace_back(i + 1, j + 1); } } } cout << e.size() << "\n"; for (auto [x, y] : e) { cout << x << " " << y << "\n"; } return 0; }
E. Increasing Frequency
思路
将问题转化为寻找一个区间 \([l, r]\),使得该区间内某个数 \(x\) 的出现次数与 \(c\) 的出现次数的差最大。
定义前缀差 \(\text{pre}[x]\) 为前 \(j\) 项中 \(x\) 的出现次数减去 \(c\) 的出现次数,并维护每个 \(x\) 的最小前缀差 \(\text{Min}[x]\)。遍历数组时,对于当前位置 \(j\),若当前元素为 \(x\),则 \(\text{delta} = \text{pre}[x] - \text{pre}[c] - \text{Min}[x]\),即区间 \((l, j]\) 内 \(x\) 比 \(c\) 多出的数量。最终答案为原数组中 \(c\) 的数量加上所有 \(\text{delta}\) 的最大值即可。时间复杂度为 \(O(n)\)。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, c; cin >> n >> c; vector<int> a(n); for (auto &i : a) { cin >> i; } int idx = 0; vector<int> res(n); unordered_map<int, int> pre, Min; for (auto i : a) { Min[i] = min(Min[i], pre[i] - pre[c]); pre[i] ++; res[idx++] = pre[i] - pre[c] - Min[i]; } int ans = pre[c]; for (int i = 0; i < n; i ++) { ans = max(ans, res[i] + pre[c]); } cout << ans << "\n"; return 0; }
本文作者:Ke_scholar
本文链接:https://www.cnblogs.com/Kescholar/p/18718010
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步