AtCoder Beginner Contest 329
A - Spread
隔一个字符输出一个空格
时间复杂度 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);
std::string s;
std::cin >> s;
for (int i = 0; i < s.size(); i++) {
std::cout << s[i] << " ";
}
return 0;
}
B - Next
找出严格次大值,排序一下倒序枚举就好了
时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN)
#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;
std::vector<int> a(N);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end(), std::greater());
for (int i = 0; i < N; i++) {
if (a[i] != a[0]) {
std::cout << a[i] << "\n";
return 0;
}
}
return 0;
}
C - Count xxx
统计该字符串中不同子串的个数,同时子串的要求为必须是同一个字符的重复,如 a a , a a a aa,aaa aa,aaa,而 a b ab ab就不属于答案集合。
所以就只要知道每个字符在字符串中最多连续出现多少次就好了,最终答案就是每个字符最大连续长度相加。
时间复杂度 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;
std::cin >> N;
std::string S;
std::cin >> S;
i64 ans = 0;
std::array<int, 26> num{};
for (int i = 0, j = 0; i < N; ) {
while (j < N && S[i] == S[j]) {
j++;
}
int len = j - i;
int now = S[i] - 'a';
num[now] = std::max(num[now], len);
i = j;
}
for (int i = 0; i < 26; i++) {
ans += num[i];
}
std::cout << ans << "\n";
return 0;
}
D - Election Quick Report
其实就是要动态地统计当前哪个位置上的贡献最大,由于 N N N的范围只有 2 E 6 2E6 2E6,所以我们可以在每个位置上开一个桶,而引起最大值变化只有可能是在某一个位置贡献+1时,当前位置成为贡献最大的位置。
时间复杂度 O ( N + M ) O(N+M) O(N+M)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::vector<int> A(M);
std::vector<int> B(N);
for (int i = 0; i < M; i++) {
std::cin >> A[i];
A[i]--;
}
int max = 0, ans = -1;
for (int i = 0; i < M; i++) {
if (++B[A[i]] > max) {
max = B[A[i]];
ans = A[i];
} else if (B[A[i]] == max && A[i] < ans) {
ans = A[i];
}
std::cout << ans + 1 << "\n";
}
return 0;
}
E - Stamp
正难则反,我们考虑能否将 S S S串变回一个空串,就类似于一层层地撕下 T T T串。
拿sample1举例, A B C B A B C → # # # B A B C → # # # B # # # → # # # # # # ABCBABC \to \#\#\#BABC \to \#\#\#B\#\#\# \to \#\#\#\#\#\# ABCBABC→###BABC→###B###→######,也就是说先前被撕掉的位置是可以继续撕下去的,正着来想其实就是后面贴上的串将先前贴上的串给覆盖掉了。
此时如果我们每次暴力地扫一遍哪个位置能扫,时间复杂度为 O ( N 2 ) O(N^{2}) O(N2),显然是会超时的,因此重点需要我们考虑如何进行优化。
每次撕掉一个 T T T串之后,我们考虑临近位置能否撕掉并将其加入队列。
最终至多会有 N N N个元素弹出队列。
时间复杂度 O ( N M 2 ) O(NM^{2}) O(NM2)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::string S, T;
std::cin >> S >> T;
std::vector<bool> vis(N);
auto solve = [&](int x) -> bool {
if (vis[x]) {
return false;
}
for (int i = 0; i < M; i++) {
if (T[i] != S[x + i] && S[x + i] != '#') {
return false;
}
}
for (int i = 0; i < M; i++) {
S[x + i] = '#';
}
vis[x] = true;
return true;
};
std::queue<int> q;
for (int i = 0; i < N - M + 1; i++) {
if (solve(i)) {
q.push(i);
}
}
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = std::max(0, x - M); i <= std::min(N - M, x + M); i++) {
if (solve(i)) {
q.push(i);
}
}
}
if (S == std::string(N, '#')) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
return 0;
}
F - Colored Ball
启发式合并模板题。
我们每次将物品少的集合放到物品多的集合中,然后按题意交换集合下标,我们可以直接使用std::swap
#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> C(N);
for (int i = 0; i < N; i++) {
std::cin >> C[i];
}
std::vector<std::set<int>> f(N);
for (int i = 0; i < N; i++) {
f[i].emplace(C[i]);
}
while (Q--) {
int u, v;
std::cin >> u >> v;
u--, v--;
if (f[u].size() > f[v].size()) {
std::swap(f[u], f[v]);
}
for (auto x : f[u]) {
f[v].emplace(x);
}
f[u].clear();
std::cout << f[v].size() << "\n";
}
return 0;
}
时间复杂度 O ( ( N + Q ) l o g N ) O((N+Q)logN) O((N+Q)logN)
关于本题时间复杂度的证明:
对于每个元素所在的集合, s i z ( a ) + s i z ( b ) ≤ 2 ∗ s i z ( b ) siz(a) + siz(b) \le 2 * siz(b) siz(a)+siz(b)≤2∗siz(b),因此每个元素至多被操作 O ( l o g N ) O(logN) O(logN)次,复杂度得证。