AtCoder Beginner Contest 386(补题)

AtCoder Beginner Contest 386

C - Operate 1

https://atcoder.jp/contests/abc386/tasks/abc386_c

思路

简单的条件判断题

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int k;
	std::string s, t;
	std::cin >> k >> s >> t;

	int n = s.size(), m = t.size(), ans = 0;

	if (n == m){
		for (int i = 0; i < n; i++){
			if (s[i] != t[i]){
				k -= 1;
				if (k < 0) {
					break;
				}
			}
		}
		if (k < 0) ans = 0;
		else ans = 1;
	}else if (n == m + 1){
		int index = 0;
		for (int i = 0; i < n; i++){
			if (s[i] == t[index]){
				index++;
			}
		}
		if (index == m){
			ans = 1;
		}else{
			ans = 0;
		}
	}else if (m == n + 1){
		int index = 0;
		for (int i = 0; i < m; i++){
			if (t[i] == s[index]) index++;
		}
		if (index == n){
			ans = 1;
		}else{
			ans = 0;
		}
	}else{
		ans = 0;
	}

	std::cout << (ans == 0 ? "No\n" : "Yes\n");

}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

D - Diagonal Separation

https://atcoder.jp/contests/abc386/tasks/abc386_d

思路

通过观察,可以发现,最后的棋盘中,黑色格子形成了一种阶梯状(左上三角形)的结构。也就是说,一个黑格子的左上的所有格子,必须都是黑格子。所以,如果一个已染色的黑格子的左上有白格子,那么一定是No。因此,“染色的黑格子的左上没有染色的白格子”是一个必要条件。

那么它是不是充分条件呢?是!只要我把所有的黑格子的左上全染黑,就得到了一个可行方案,因此该条件是充分条件,只要满足了,就可以输出Yes。

因此,只需检测每个黑格子的左上有没有白格子即可。暴力检测复杂度M^2会超时,可以考虑先把所有格子按照行升序排序,如果行相同就按照列升序,依次处理。

评述

最近在学离散化,看到这个N的范围还以为是离散化,上来就是做,结果题目都没有理解。【哭了】

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

struct node{
	int x, y;
	bool is_b;
};

bool operator<(node a, node b){
	return a.x != b.x ? a.x < b.x : a.y < b.y;
}

void solve(){
	int n, m;
	std::cin >> n >> m;

	std::vector<node> v(m);

	for (int i = 0; i < m; i++){
		int a, b;
		char c;
		std::cin >> a >> b >> c;
		v[i] = {a, b, c == 'B'};
	}

	std::sort(v.begin(), v.end());

	int mn = n + 1;

	for (auto nd : v){
		if (nd.is_b){
			if (nd.y >= mn){
				std::cout << "No\n";
				return;
			}
		}else{
			mn = std::min(mn, nd.y);
		}
	}

	std::cout << "Yes\n";
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

E - Maximize XOR

https://atcoder.jp/contests/abc386/tasks/abc386_e

思路

根据题目的所给的\(C(n, k) \leq 10^6\),所以直接枚举所以情况就行。时间复杂度是\(K * O(C(n, k))\),但是考虑到 k 有可能很大,导致TLE。我们知道\(C(n, k) = C(n, n - k)\),所以可以通过比较 k 和 n/2 的大小关系选择两种枚举方案,一种是在 n 个数中选 k 个,一种是 n 个数都选了,其中 n - k 个数需要去掉

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, k;
	std::cin >> n >> k;

	std::vector<i64> v(n+1);
	for (int i = 1; i <= n; i++){
		std::cin >> v[i];
	}

	i64 ans = 0;

	auto dfs = [&](auto self, int pos, int left, i64 sum) -> void {
		if (left == 0){
			ans = std::max(ans, sum);
			return;
		}

		for (int i = pos + 1; i <= n; i++){
			self(self, i, left - 1, sum ^ v[i]);
		}
	};

	if (k < n / 2)
		dfs(dfs, 0, k, 0);
	else{
		i64 presum = 0;
		for (int i = 1; i <= n; i++) presum ^= v[i];
		dfs(dfs, 0, n - k, presum);
	}

	std::cout << ans << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

F - Operate K

https://atcoder.jp/contests/abc386/tasks/abc386_f

思路:dp + 优化

这道题目是一道经典 DP 问题:
假设 S 的长度是 N,T 的长度是 M
\(dp[i][j]\) 代表将 \(S[1...i]\) 改成和 \(T[1...j]\) 一样,最少需要几次操作。
这样只需要判断 \(dp[N][M]\) 是否 <= K 即可。
转移方程:
$dp[i][j] $= min(

​ 删除一个字符,即 \(dp[i-1][j]+1\)

​ 添加一个字符,即 \(dp[i][j-1]+1\)

​ 更改一个字符,即 \(dp[i-1][j-1]+1\)

​ 这两个字符本身就匹配,要求 \(S[i] == T[j]\),此时对应 \(dp[i-1][j-1]\)
)
这样做的复杂度是 \(O(NM)\) 的,会超时。

但是注意到 K 很小,代表着 \(dp[i][j]\) 中,如果 \(|i-j| > k\),那么需要的操作次数一定比 K 多,也即,此时 \(dp[i][j]\) 一定 > k,计算它没有意义。
所以,我们只考虑 \(|i-j| <= k\) 的 dp。这样的复杂度就是 O(NK) 的,\(j\)的值被\(i\)约束,可行的\(j\)的范围是\([i-30, i+30]\),所以直接从这个范围的起点开始枚举\(j\)就行了

评述

相比于做对这道题,学会这道题的优化技巧更加重要

代码

#include <bits/stdc++.h>
using namespace std;

int k;
int n, m;
string s, t;

int f[500010][60];	// f[i][x] 是 dp[i][i-30+x]
int get_dp(int i, int j) {
	// 输入 i, j,返回 dp[i][j]
	if (abs(i-j) > k) return 0x3f3f3f3f;
	return f[i][j-i+30];
}

void solve() {
	memset(f, 0x3f, sizeof(f));
	f[0][30] = 0;	// 对应 dp[0][0]
	for (int i = 1; i <= k; ++i)
		f[0][30+i] = i;	// f[0][30+i] 对应 dp[0][i]
	for (int i = 1; i <= k; ++i)
		f[i][30-i] = i;	// f[i][30-i] 对应 dp[i][0]

	for (int i = 1; i <= n; ++i)
		for (int x = 0; x < 60; ++x) {
			int j = i-30+x;
			if (j <= 0 || j > m)
				continue;
			int cur_dp = 0x3f3f3f3f;
			cur_dp = min(cur_dp, get_dp(i-1, j)+1);
			cur_dp = min(cur_dp, get_dp(i, j-1)+1);
			cur_dp = min(cur_dp, get_dp(i-1, j-1)+1);
			if (s[i] == t[j])
				cur_dp = min(cur_dp, get_dp(i-1, j-1));
			f[i][x] = cur_dp;
		}
}

int main() {
	cin >> k;
	cin >> s >> t;
	n = s.length();
	m = t.length();
	s = " " + s;
	t = " " + t;

	solve();
	puts(get_dp(n, m) <= k ? "Yes" : "No");

	return 0;
}
posted @ 2024-12-31 15:35  califeee  阅读(93)  评论(0)    收藏  举报