AtCoder Beginner Contest 328
A - Not Too Hard
就是比较大小然后把满足条件的值加进来
时间复杂度 O ( N ) O(N) O(N)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, X;
std::cin >> N >> X;
int ans = 0;
for (int i = 0; i < N; i++) {
int x;
std::cin >> x;
if (x <= X) {
ans += x;
}
}
std::cout << ans << "\n";
return 0;
}
B - 11/11
这道题我做的时候很麻烦,甚至还把每一位都拆出来了,实际上只要把月份和当前的是哪一天转成字符串加起来比较一下整个串的每个字符是否都等于这个这个串的第一个字符就好了。
时间复杂度 O ( N D ) O(ND) O(ND)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
int ans = 0;
for (int i = 1; i <= N; i++) {
int D;
std::cin >> D;
for (int j = 1; j <= D; j++) {
auto s = std::to_string(i) + std::to_string(j);
if (s == std::string(int(s.size()), s[0])) {
ans += 1;
}
}
}
std::cout << ans << "\n";
return 0;
}
C - Consecutive
看一个区间内满足 s i = s i + 1 s_i = s_{i + 1} si=si+1条件的字符有多少个,显然就是要做一个前缀和,这里注意不能把这个区间内最后一个字符放进去。
时间复杂度 O ( N + Q ) O(N + Q) O(N+Q)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, Q;
std::cin >> N >> Q;
std::string S;
std::cin >> S;
S.push_back(' ');
std::vector<int> sum(N + 1);
for (int i = 0; i < N; i++) {
sum[i + 1] = sum[i] + (S[i] == S[i + 1]);
}
while (Q--) {
int l, r;
std::cin >> l >> r;
l--, r--;
std::cout << sum[r] - sum[l] << "\n";
}
return 0;
}
D - Take ABC
从左边开始每次都删除ABC
这个字符串,每次都从左边删,其实已经暗示的非常明显了,就是要用栈。每次字符串后端为ABC
时就删掉。
时间复杂度 O ( 3 N ) O(3N) O(3N)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string S;
std::cin >> S;
std::string T;
for (auto c : S) {
T += c;
while (T.size() >= 3 && T.substr(int(T.size()) - 3) == "ABC") {
T.resize(int(T.size()) - 3);
}
}
std::cout << T << "\n";
return 0;
}
E - Modulo MST
题目是要求一个最小生成树,但是是在模数意义下的,因此就不能靠平时用的最小生成树算法来解决了。
数据范围 1 ≤ M ≤ 28 1 \le M \le 28 1≤M≤28,看起来就是可以爆搜的,但是要考虑剪枝,由于生成树最多有 n − 1 n-1 n−1条边,所以其实最多就会枚举7条边,时间复杂度上限为KaTeX parse error: Undefined control sequence: \C at position 3: O(\̲C̲_{28}^{7})
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
i64 K;
std::cin >> N >> M >> K;
std::vector<std::tuple<int, int, i64>> e(M);
for (int i = 0; i < M; i++) {
int u, v;
i64 w;
std::cin >> u >> v >> w;
u--, v--;
e[i] = {u, v, w};
}
i64 ans = i64(1E15);
int u, v;
i64 w;
for (int s = 0; s < (1 << M); s++) {
i64 res = 0;
DSU dsu(N);
for (int j = 0; j < M; j++) {
std::tie(u, v, w) = e[j];
if (s >> j & 1) {
if (!dsu.same(u, v)) {
dsu.merge(u, v);
res = (res + w) % K;
} else {
break;
}
}
}
if (dsu.size(0) == N) {
ans = std::min(ans, res);
}
}
std::cout << ans << "\n";
return 0;
}
F - Good Set Query
带权并查集非常经典的板子题,就是给两条边连线的时候比较关键。
可以参考带权并查集进行学习,文章写的非常棒。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, Q;
std::cin >> N >> Q;
std::vector<int> f(N);
std::iota(f.begin(), f.end(), 0);
std::vector<i64> dis(N);
auto find = [&](auto self, int x) -> int {
if (x == f[x]) {
return x;
}
int y = self(self, f[x]);
dis[x] += dis[f[x]];
f[x] = y;
return y;
};
for (int i = 1; i <= Q; i++) {
int a, b, d;
std::cin >> a >> b >> d;
a--, b--;
int x = find(find, a);
int y = find(find, b);
if (x != y) {
dis[x] = dis[b] + d - dis[a];
f[x] = y;
std::cout << i << " ";
} else if (dis[a] - d == dis[b]) {
std::cout << i << " ";
}
}
return 0;
}
G - Cut and Reorder
由 1 ≤ N ≤ 22 1 \le N \le 22 1≤N≤22的数据范围容易想到可能会是个状态压缩的DP。
题目中给出的两种操作实际上是相互独立的,于是可以考虑DP。
普通地,我们考虑设计DP状态,记 d p [ i ] [ s ] dp[i][s] dp[i][s]为前 i i i个数,匹配状态为 s s s的最小代价( s s s在二进制下1代表匹配,0代表未匹配)
我们既要考虑单个去匹配又要考虑成块去匹配,这一步操作的复杂度是 O ( N 2 ) O(N^{2}) O(N2),所以总的时间复杂度为 O ( 2 N N 3 ) O(2^{N}N^{3}) O(2NN3),这样显然是会超时的。
但是注意到我们枚举状态 s s s是从 0 → 2 n 0 \to 2^{n} 0→2n,其中二进制中1的个数是非严格递增的,因此可以借此去优化掉一维枚举。
于是记 d p [ s ] dp[s] dp[s]为当前 a a a数组中前 p o p c o u n t ( s ) popcount(s) popcount(s)个数已经与以 s s s的二进制中1的位置为下标的 b b b数组中元素相匹配。
因此每匹配一部分就要加上这一部分数字移动的代价以及达到匹配效果所需要的差值。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
i64 C;
std::cin >> N >> C;
std::vector<i64> A(N);
for (int i = 0; i < N; i++) {
std::cin >> A[i];
}
std::vector<i64> B(N);
for (int i = 0; i < N; i++) {
std::cin >> B[i];
}
std::vector<i64> dp(1 << N, 1E18);
dp[0] = -C;
for (int s = 0; s < (1 << N); s++) {
int c = __builtin_popcount(s);
for (int i = 0; i < N; i++) {
i64 val = 0;
int t = s;
if (~s >> i & 1) {
for (int j = i; j < N; j++) {
if (s >> j & 1) {
break;
}
t |= 1 << j;
val += std::abs(A[c + j - i] - B[j]);
dp[t] = std::min(dp[t], dp[s] + val + C);
}
}
}
}
std::cout << dp.back() << "\n";
return 0;
}