EPIC Institute of Technology Round Summer 2025 (Codeforces Round 1036, Div. 1 + Div. 2)
A. Deranged Deletions
题意:给你一个数组\(a\),你可以删除一些元素。最后要求排序后的这个数组和这个数组没有一个位置是相同的。
留下两个数满足第一个数大于第二个数就行。可以暴力枚举。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < n; ++ i) {
for (int j = i + 1; j < n; ++ j) {
if (a[i] > a[j]) {
std::cout << "YES\n";
std::cout << 2 << "\n";
std::cout << a[i] << " " << a[j] << "\n";
return;
}
}
}
std::cout << "NO\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
B. Minimise Sum
题意:给你一个数组,你可以操作一次,选择\(i, j\)满足\(i < j\),然后把\(a_j\)加到\(a_i\)上,之后\(a_j = 0\)。求\(\sum_{i=1}^{n} min(a_1, .. ,a_i)\)最小。
显然操作后\([j, n]\)对应位置的前缀的最小值都是\(0\)。那么可以枚举\(j\)是哪个位置,除了\(a_2\)必须加到\(a_1\)上会影响\([1, 1]\)的最小值,其它都可以加到最大值的位置使得最小值不受影响。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
i64 sum = a[0] + std::min(a[0], a[1]), min = std::min(a[0], a[1]);
i64 ans = a[0] + a[1];
for (int i = 2; i < n; ++ i) {
ans = std::min(ans, sum);
min = std::min(min, a[i]);
sum += min;
}
ans = std::min(ans, sum);
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
C. Subset Multiplication
题意:给你一个数组\(b\),求一个\(x\),使得可以选出一些位置除以\(x\)后得到数组\(a\),所有\(a_i\)整除\(a_{i+1}\)。求一个合法的\(x\)。
\(b_i\)要么等于\(a_i\)要么等于\(a_i \times x\),可以记\(t_i\)表示是否乘了\(x\),\(t_i = 0\)则没有乘,\(t_i = 1\)则乘了。
那么\(b_i = a_i \times x^{t_i}\)。记\(a_{i+1} = a_i \times c\)。那么\(gcd(b_i, b_{i+1}) = gcd(a_i \times x^{t_i}, a_{i} \times c \times x^{t_{i+1}}) = a_i \times x^{min(t_i, t_{i+1})}\)
我们用\(b_i\)除这个东西,就是\(\frac{a_i \times x^{t_i}}{a_i \times x^{min(t_i, t_{i+1}})} = x^{t_i - min(t_i, t_{i+1})}\),显然这个值要么是\(x\)要么是\(1\)。但不同相邻对得到的\(x\)可能不同,我们可以取它们的最小公倍数。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<i64> b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
}
i64 x = 1;
for (int i = 0; i + 1 < n; ++ i) {
i64 d = std::gcd(b[i], b[i + 1]);
i64 v = b[i] / d;
if (v > 1) {
x = x / std::gcd(x, v) * v;
if (x > 1e9) {
x = v;
}
}
}
std::cout << x << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
D. Make a Palindrome
题意:给你一个数组,每次选择一个长度大于等于\(k\)的子区间,然后删掉第\(k\)小的数,如果有多少可以删任意一个。求能不能使得数组变成回文。
显然原数组第\(1\)到\(k-1\)小不可能被删掉,假设得到了一个结果,那么我们可以把第\(k+1\)小及比它大的都删掉,依然符合条件。那么我们把第\(1\)到第\(k\)小的位置拿出来,问题变为删掉一些第\(k\)小的位置使得数组变成回文,注意数组长度最少为\(k - 1\)。
这个可以通过双指针判断,每次找两侧不等于第\(k\)小的数,因为它们不能被删,所以每次找到的对必须相同。然后记录两侧第\(k\)小的数的个数的差异,因为我们要删去两侧一些数使得构成回文。那么删去的总数不能使得长度小于\(k-1\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
auto b = a;
std::ranges::sort(b);
int x = b[k - 1];
std::vector<int> c;
for (int i = 0; i < n; ++ i) {
if (a[i] <= x) {
c.push_back(a[i]);
}
}
int m = c.size();
int l = 0, r = m - 1;
int len = m;
while (l < r) {
int cntl = 0;
while (l < r && c[l] == x) {
++ l;
++ cntl;
}
int cntr = 0;
while (r >= 0 && c[r] == x) {
-- r;
++ cntr;
}
if (l > r) {
std::cout << "YES\n";
return;
}
if (c[l] != c[r]) {
std::cout << "NO\n";
return;
}
len -= std::abs(cntl - cntr);
if (len < k - 1) {
std::cout << "NO\n";
return;
}
++ l, -- r;
}
std::cout << "YES\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}