AtCoder Beginner Contest 329

本文介绍了AtCoderBeginnerContest329中的五个编程题目,涉及字符串处理、查找次大值、统计子串个数、动态贡献统计和字符串操作。每个题目都强调了其时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)2siz(b),因此每个元素至多被操作 O ( l o g N ) O(logN) O(logN)次,复杂度得证。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值