AtCoder Beginner Contest 326(A~F) abc326题解

AtCoder Beginner Contest 326

A - 2UP3DOWN

根据题意模拟一下就好了,注意上下值不同。

时间复杂度 O ( 1 ) O(1) O(1)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int X, Y;
	std::cin >> X >> Y;

	if ((Y - X > 0 && Y - X <= 2) || (Y - X < 0 && X - Y <= 3)) {
		std::cout << "Yes\n";
	} else {
		std::cout << "No\n";
	}

	return 0;
}
B - 326-like Numbers

由于数据范围很小,直接枚举一下从当前数开始满足题目所述条件的第一个数就好了。

时间复杂度 O ( 1 ) O(1) O(1)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int x;
	std::cin >> x;

	for (int i = x; ;i++) {
		std::string s = std::to_string(i);

		int a = s[0] - '0';
		int b = s[1] - '0';
		int c = s[2] - '0';

		if (a * b == c) {
			std::cout << a << b << c << "\n";
			return 0;
		}
	}

	return 0;
}
C - Peak

实际上就是要选定一个 x x x点,要求使以 x x x为起点的长度为 M M M的区间所覆盖的值最多,那么这个 x x x点显然就是从一个 A i A_i Ai开始最优,所以只需要枚举一下这 N N N A i A_i Ai为起点的区间就好了,个数的话其实就是二分求右端点的位置。

时间复杂度 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, M;
	std::cin >> N >> M;

	std::vector<int> A(N);
	for (int i = 0; i < N; i++) {
		std::cin >> A[i];
	}

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

	int ans = 0;
	for (int i = 0; i < N; i++) {
		int p = std::upper_bound(A.begin(), A.end(), A[i] + M - 1) - A.begin() - 1;
		// std::cerr << i << " " << p << "\n";
		ans = std::max(ans, p - i + 1);
	}

	std::cout << ans << "\n";

	return 0;
}
D - ABC Puzzle

题目要求构造一个每行第一个字符组成 R R R串,每列第一个字符组成 C C C串的矩阵,由于矩阵很小,所以显然是可以爆搜的。

由于题目要求每行每列仅存在 一个 A B C ABC ABC字符,所以对于每行每列我们都利用状态压缩记录一下当前行/列是否存在某个字符。

接下来只需要枚举矩阵中的每个位置就好了。

不过这个题的写法需要学习一下,当这一行/列的状态为0时说明还没有填入字符,此时判断一下当前拟填入的字符是否为需要填入的字符;其次还要判断一下这一字符在这一行/列是否已经存在了,这些都使用了状态压缩的技巧,写起来很方便,值得学习。

时间复杂度有点玄学,因为包含了大量剪枝。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N;
	std::string R, C;
	std::cin >> N >> R >> C;

	std::vector a(N, std::string(N, '.'));

	std::vector<int> row(N), col(N);

	int tot = 0;
	auto dfs = [&](auto self, int x, int y) -> void {
		if (x == N) {
			if (tot == 3 * N) {
				std::cout << "Yes\n";
				for (int i = 0; i < N; i++) {
					std::cout << a[i] << "\n";
				}
				std::exit(0);
			}
			return;
		}

		if (y == N) {
			return self(self, x + 1, 0);
		}

		self(self, x, y + 1);
		for (int i = 0; i < 3; i++) {
			if (row[x] == 0 && R[x] != i + 'A') {
				continue;
			}
			if (col[y] == 0 && C[y] != i + 'A') {
				continue;
			}
			if (row[x] >> i & 1) {
				continue;
			}
			if (col[y] >> i & 1) {
				continue;
			}

			a[x][y] = 'A' + i;
			row[x] ^= 1 << i;
			col[y] ^= 1 << i;
			tot += 1;
			self(self, x, y + 1);
			row[x] ^= 1 << i;
			col[y] ^= 1 << i;
			a[x][y] = '.';
			tot -= 1;
		}
	};
	dfs(dfs, 0, 0);

	std::cout << "No\n";
	return 0;
}
E - Revenge of “The Salary of AtCoder Inc.”

这题有两种思考方式: 直接找规律考虑每个数概率的贡献/DP地考虑每个数的概率

首先,投掷到每个数的概率都是 1 n \frac{1}{n} n1

DP地考虑每个数的概率:

因为只能由小数转移到大数,所以显然有 d p [ i ] = 1 n ∑ 0 i − 1 d p [ j ] dp[i]=\frac{1}{n}\sum^{i-1}_{0}dp[j] dp[i]=n10i1dp[j]

这里 1 n d p [ j ] \frac{1}{n}dp[j] n1dp[j]代表当 x = j x=j x=j时,骰子投到 i i i的概率。

当然这里还要做一个类似前缀和的东西,因为小点数的点一定会经过大点数的点,也会有相应代价。

时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>

using i64 = long long;

constexpr int P = 998244353;
int power(i64 a, int b) {
	i64 res = 1 % P;
	for (; b; b /= 2, a = (a * a) % P) {
		if (b % 2) {
			res = (res * a) % P;
		}
	}
	return res;
}
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::vector<int> dp(N);
	auto inv = power(N, P - 2);

	i64 sum = inv;
	dp[0] = inv;
	for (int i = 1; i < N; i++) {
		dp[i] = (sum * inv) % P;
		sum = (sum + dp[i]) % P;
	}

	int ans = 0;
	sum = 0;
	for (int i = 0; i < N; i++) {
		sum = (sum + dp[i]) % P;
		ans = (1LL * sum * A[i] + ans) % P;
	}

	std::cout << ans << "\n";
	return 0;
}

直接找规律考虑每个数概率的贡献,详见https://www.luogu.com.cn/problem/solution/AT_abc326_e第一篇题解。

#include <bits/stdc++.h>

using i64 = long long;

constexpr int P = 998244353;
i64 power(i64 a, int b) {
	i64 res = 1 % P;
	for (; b; b /= 2, a = (a * a) % P) {
		if (b % 2) {
			res = (res * a) % P;
		}
	}

	return res;
}

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];
	}

	auto inv = power(N, P - 2);
	std::vector<int> dp(N);

	dp[0] = inv;
	for (int i = 1; i < N; i++) {
		dp[i] = (1LL * dp[i - 1] + (1LL * dp[i - 1] * inv) % P) % P;
	}

	int ans = 0;
	for (int i = 0; i < N; i++) {
		ans = ((1LL * dp[i] * A[i]) % P + ans) % P;
	}

	std::cout << ans << "\n";
}
F - Robot Rotation

看这题一眼就能想出一个 O ( 2 80 ) O(2^{80}) O(280)的暴力做法,但显然是会超时的。

考虑这个题的特点其实可以发现,奇数步会走y轴,偶数步会走x轴。

因此可以拆分成两个方向进行暴力,时间复杂度 O ( 2 40 ) O(2^{40}) O(240),这样还是会超时,不过如果优化到 2 20 2^{20} 220是一定可以过的,考虑 m e e t   i n   t h e   m i d d l e meet \ in \ the \ middle meet in the middle算法,就是搜一半停下,然后以当前这一半的终点为起点去搜另一半,这样就能极大地优化复杂度。
通过此题也能学到一种通过dfs寻找路径答案的剪枝策略。

时间复杂度 O ( 2 n 4 ) O(2^{\frac{n}{4}}) O(24n)

#include <bits/stdc++.h>

using i64 = long long;

std::vector<int> work(std::vector<int> A, int goal) {
	int n = A.size();
	int n1 = n / 2;

	std::vector<std::pair<int, int>> path;
	path.reserve(1 << n1);
	auto dfs1 = [&](auto self, int x, int s, int mask) -> void {
		if (x == n1) {
			path.emplace_back(s, mask);
			return;
		}

		self(self, x + 1, s - A[x], mask);
		self(self, x + 1, s + A[x], mask | (1 << x));
	};
	dfs1(dfs1, 0, 0, 0);

	std::sort(path.begin(), path.end());
	std::vector ans{-1};
	auto dfs2 = [&](auto self, int x, int s, int mask) -> bool {
		if (x == n) {
			auto it = std::lower_bound(path.begin(), path.end(), std::make_pair(goal - s, 0));
			if (it != path.end() && it->first == goal - s) {
				ans.clear();
				for (int i = 0; i < n1; i++) {
					ans.emplace_back(it->second >> i & 1);
				}
				for (int i = 0; i < n - n1; i++) {
					ans.emplace_back(mask >> i & 1);
				}
				return true;
			}
			return false;
		}

		if (self(self, x + 1, s - A[x], mask)) {
			return true;
		}
		if (self(self, x + 1, s + A[x], mask | (1 << (x - n1)))) {
			return true;
		}

		return false;
	};
	dfs2(dfs2, n1, 0, 0);

	return ans;
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int N, X, Y;
	std::cin >> N >> X >> Y;

	std::vector<int> A(N);
	std::vector<int> Ax, Ay;
	for (int i = 0; i < N; i++) {
		std::cin >> A[i];
		if (i % 2) {
			Ax.push_back(A[i]);
		} else {
			Ay.push_back(A[i]);
		}
	}

	auto ax = work(Ax, X);
	auto ay = work(Ay, Y);

	if (ax == std::vector{-1} || ay == std::vector{-1}) {
		std::cout << "No\n";
		return 0;
	}

	std::cout << "Yes\n";
	std::vector<int> ans;
	for (int i = 0; i < ay.size(); i++) {
		ans.push_back(ay[i]);
		if (i < ax.size()) {
			ans.push_back(ax[i]);
		}
	}

	int direct = 1;
	for (int i = 0; i < N; i++) {
		std::cout << "LR"[ans[i] ^ direct ^ (i & 1)];
		direct = ans[i];
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值