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]=n1∑0i−1dp[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;
}