ABC328 现场
被所有人吊打,bykem 20min 切到 G,找时间强迫他 v 个挂。
失误比较多,做太慢了,手速选手变成老人是这样的。
场切
A
题意:给定一个长度为 \(n\) 的序列 \(s\),求其中不超过 \(x\) 的数之和。
Solution
直接特判,加进答案就行了。
这里我脑子炸了,对序列 \(s\) 排序后 upper_bound 找到第一个大于 \(x\) 的数,然后 accumulate 一下。当时孩子认为做的比较快,难绷。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 8 + 1;
int n, x;
int s[kN];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> x;
for (int i = 1; i <= n; i++) {
cin >> s[i];
}
sort(s + 1, s + n + 1);
cout << accumulate(s + 1, upper_bound(s + 1, s + n + 1, x), 0ll) << '\n';
return 0;
}
B
题意:有 \(n\) 个月,每个月有 \(d_i\) 天,问有多少个日期可以使用同一个数字表示?
Solution
可以直接枚举这个同一个数字,然后统计答案贡献即可。
我考场上的时候的机翻比较烂,碰巧我 ABC 很少看原文。于是顶着脑子里的错误题意调成了正确代码,其实是写错了一个地方。
傻逼有道翻译。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 100 + 1;
int n;
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n;
int ans = 0;
for (int i = 1, d; i <= n; i++) {
cin >> d;
if (i < 10) {
if (d >= i) {
ans++;
}
if (d >= 10 * i + i) {
ans++;
}
} else if (i > 1 && i / 10 == i % 10) {
if (d >= i % 10) {
ans++;
}
if (d >= i) {
ans++;
}
}
}
cout << ans << '\n';
return 0;
}
C
题意:给定一个长度为 \(n\) 的字符串 \(s\),有 \(q\) 次询问,求区间相邻相等字符位置个数。
Solution
直接对相邻相等位置前缀和一下就好了。
第一道没有失误的题。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 3e5 + 1;
int n, q;
string s;
int a[kN];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> q >> s, s = '#' + s;
for (int i = 2; i <= n; i++) {
a[i] = a[i - 1];
if (s[i] == s[i - 1]) {
a[i]++;
}
}
for (int _ = 1, l, r; _ <= q; _++) {
cin >> l >> r;
cout << a[r] - a[l] << '\n';
}
return 0;
}
D
题意:给定一个字符串 \(s\),每次找到最左端出现的 "ABC" 子串删除。问操作至无法操作后的字符串。
Solution
把字符从左往右加入栈中,"ABC" 一定是栈顶的连续三个字符。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
int n;
string s;
vector<char> v;
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> s;
n = s.size(), s = '#' + s;
v.emplace_back(0);
for (int i = 1, c = 0; i <= n; i++) {
v.emplace_back(s[i]), c++;
if (c >= 3 && v[c - 2] == 'A' && v[c - 1] == 'B' && v[c] == 'C') {
v.pop_back(), v.pop_back(), v.pop_back();
c -= 3;
}
}
for (int i = 1; i < v.size(); i++) {
cout << v[i];
}
return 0;
}
E
题意:给定一个 \(n\) 个点 \(m\) 条边的无向联通图,求他在 \(\mod k\) 意义下的最小生成树。
Solution
数据范围这么小,直接爆搜连哪些边即可。
为了防止连出环,并查集判一下即可。其实可以用可撤销并查集加上按秩合并省掉一个 \(n\)。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 1e5 + 1;
LL n, m, p;
LL x[kN], y[kN], w[kN];
LL t[kN], fa[kN], ans = 1e18;
LL R(int x) {
if (x == fa[x]) {
return x;
}
fa[x] = R(fa[x]);
return fa[x];
}
void D(LL k, LL c, LL d) {
if (d == n - 1 || m - k + 1 < n - 1 - d) {
if (m - k + 1 < n - 1 - d) {
return;
}
int f = 0;
iota(fa, fa + n + 1, 0);
for (int i = 1; i <= m; i++) {
if (t[i]) {
if (R(x[i]) == R(y[i])) {
f = 1;
} else {
fa[R(x[i])] = R(y[i]);
}
}
}
if (!f) {
ans = min(ans, c);
}
return;
}
t[k] = 1, D(k + 1, (c + w[k]) % p, d + 1);
t[k] = 0, D(k + 1, c, d);
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m >> p;
for (int i = 1; i <= m; i++) {
cin >> x[i] >> y[i] >> w[i];
}
D(1, 0, 0);
cout << ans << '\n';
return 0;
}
F
题意:给定 \(q\) 个三元组 \((a_i, b_i, d_i)\),按顺序考虑,如果存在一个序列 \(x\) 使之前选中的以及当前这个三元组所表示的条件 \(x_{a_i} - x_{b_i} = d_i\),那么就选中当前三元组。问最后选择了哪些三元组?
Solution
考虑并查集维护互相数值关系确定的连通块。\(fa_i\) 表示 \(i\) 的祖先,令 \(g_i = x_i - x_{fa_i}\)。然后合并时和路径压缩时更新 \(fa\) 和 \(g\) 即可。
考虑加入 \(x_i - x_j = k\) 这个条件。发现只有 \(i\) 和 \(j\) 在同一个连通块内且 \(g_i - g_j != k\) 时不可加入限制。合并时考虑把 \(j\) 并向 \(i\)。有 \(x_{fa_j} = x_j - g_j, x_j = x_i - k, x_i = x_{fa_i} + g_i\),\(x_{fa_j} = x_{fa_i} + g_i - k - g_j\),\(g_{fa_i} = g_i - g_j - k\)。但是在 \(j\) 连向 \(i\) 后 \(j\) 连通块内的其他点的 \(g\) 没有被重新计算。但是这件事情可以在路径压缩的时候完成:\(g_c\) 就等于 \(c\) 在连通块内路径压缩的道路上的点的 \(g\) 之和。
其实可以套个按秩合并,优化到反阿克曼。懒得再想了。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e5 + 1;
int n, q;
int fa[kN];
LL p[kN];
int R(int x) {
if (x == fa[x]) {
return x;
}
int v = fa[x];
fa[x] = R(fa[x]);
p[x] += p[v];
return fa[x];
}
bool M(int x, int y, int w) {
int fx = R(x), fy = R(y);
if (fx == fy) {
return p[x] - p[y] == w;
}
fa[fy] = fx;
p[fy] = p[x] - p[y] - w;
return 1;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> q;
iota(fa, fa + n + 1, 0);
for (int _ = 1, a, b, d; _ <= q; _++) {
cin >> a >> b >> d;
if (M(a, b, d)) {
cout << _ << ' ';
}
}
return 0;
}