Welcome To Ke_scholar's|

Ke_scholar

园龄:2年1个月粉丝:30关注:10

2025-02-16 14:20阅读: 5评论: 0推荐: 0

SMU winter 2025 Personal Round 3

SMU winter 2025 Personal Round 3

A. Vasya and Book

思路

计算从起始页 \(x\) 到目标页 \(y\) 的最小按键次数。每次按键可向前或向后滚动 \(d\) 页,但不可越界。解题关键在于分析三种可能路径:

  1. 直接移动:若 \(|x - y|\) 能被 \(d\) 整除,则直接移动,次数为 \(\frac{|x - y|}{d}\)
  2. 经第一页移动:若 \(y\) 到第一页的步数 \((y-1)\) 能被 \(d\) 整除,则总次数为从 \(x\) 到第一页的步数加上从第一页到 \(y\) 的步数。
  3. 经最后一页移动:若最后一页到 \(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 中国大陆许可协议进行许可。

posted @   Ke_scholar  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起