AtCoder Beginner Contest 327
A - ab
就是看子串中是否包含ab
或ba
时间复杂度 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;
if (s.find("ab") != std::string::npos || s.find("ba") != std::string::npos) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
return 0;
}
B - A^A
虽然
B
B
B很大,但左式增长速度也非常快,手玩一下当
A
=
=
16
A == 16
A==16时就会超出
B
B
B的最大范围,注意这里使用std::pow
函数会产生精度问题,所以要自己手写一个幂函数。
时间复杂度 O ( 1 ) O(1) O(1)
#include <bits/stdc++.h>
using i64 = long long;
i64 power(i64 a, int b) {
i64 res = 1;
for (; b; b /= 2, a = a * a) {
if (b % 2) {
res *= a;
}
}
return res;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
i64 B;
std::cin >> B;
for (i64 x = 1; x <= 15; x++) {
i64 y = power(x, x);
if (y == B) {
std::cout << x << "\n";
return 0;
}
}
std::cout << "-1\n";
return 0;
}
C - Number Place
实际上就是按题意模拟一下是否满足数独,考察代码能力
时间复杂度 O ( n 2 ) O(n^{2}) O(n2)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
const int n = 9;
std::array<std::array<int, n>, n> a;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
std::cin >> a[i][j];
}
}
bool ok = true;
for (int i = 0; i < n; i++) {
std::set<int> f;
for (int j = 0; j < n; j++) {
f.emplace(a[i][j]);
}
ok &= int(f.size()) == 9;
}
for (int j = 0; j < n; j++) {
std::set<int> f;
for (int i = 0; i < n; i++) {
f.emplace(a[i][j]);
}
ok &= int(f.size()) == 9;
}
for (int i = 0; i < n; i += 3) {
for (int j = 0; j < n; j += 3) {
std::set<int> f;
for (int x = i; x < i + 3; x++) {
for (int y = j; y < j + 3; y++) {
f.emplace(a[x][y]);
}
}
ok &= int(f.size()) == 9;
}
}
if (ok) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
return 0;
}
D - Good Tuple Problem
种类并查集/染色法模板题
时间复杂度 O ( M ) O(M) O(M)
种类并查集:
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
int n;
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
x = find(x);
y = find(y);
return x == y;
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, M;
std::cin >> N >> M;
std::vector<int> A(M), B(M);
for (int i = 0; i < M; i++) {
std::cin >> A[i];
A[i]--;
}
for (int i = 0; i < M; i++) {
std::cin >> B[i];
B[i]--;
}
DSU dsu(2 * N);
for (int i = 0; i < M; i++) {
if (dsu.same(A[i], B[i]) || dsu.same(A[i] + N, B[i] + N)) {
std::cout << "No\n";
return 0;
}
dsu.merge(A[i], B[i] + N);
dsu.merge(A[i] + N, B[i]);
}
std::cout << "Yes\n";
return 0;
}
染色法:
#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), B(M);
for (int i = 0; i < M; i++) {
std::cin >> A[i];
A[i]--;
}
for (int i = 0; i < M; i++) {
std::cin >> B[i];
B[i]--;
}
std::vector<std::vector<int>> adj(N);
for (int i = 0; i < M; i++) {
adj[A[i]].push_back(B[i]);
adj[B[i]].push_back(A[i]);
}
std::vector<int> C(N, -1);
for (int i = 0; i < N; i++) {
if (C[i] == -1) {
C[i] = 0;
std::queue<int> q;
q.push(i);
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto y : adj[x]) {
if (C[y] == -1) {
C[y] = C[x] ^ 1;
q.push(y);
} else if (C[y] == C[x]) {
std::cout << "No\n";
return 0;
}
}
}
}
}
std::cout << "Yes\n";
return 0;
}
E - Maximize Rating
最开始考虑了贪心策略,但实际上是错误的。
如
3
800 1200 801
所以就要考虑DP
观察式子的结构会发现其实除了左式的分子,其他部分相对来说都是比较固定的。
记dp[i][j]
为前i
个数选择j
个数时分子的最大值,然后就类似于一个背包问题。
状态转移方程为:dp[i][j] = std::max({dp[i][j], dp[i - 1][j], dp[i - 1][j - 1] * 0.9 + P[i]})
最后枚举一下取最大值就好了
时间复杂度 O ( N 2 ) O(N^{2}) O(N2)
#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> P(N + 1);
for (int i = 1; i <= N; i++) {
std::cin >> P[i];
}
std::vector dp(N + 1, std::vector<double>(N + 1));
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= i; j++) {
dp[i][j] = std::max({dp[i][j], dp[i - 1][j], dp[i - 1][j - 1] * .9 + P[i]});
}
}
double ans = -INFINITY;
double den = 0;
for (int i = 1; i <= N; i++) {
den = den * .9 + 1;
ans = std::max(ans, dp[N][i] / den - 1200 / std::sqrt(i));
}
std::cout << std::fixed << std::setprecision(12);
std::cout << ans << "\n";
return 0;
}
F - Apples
把题目抽象出来就类似于在一个二维平面内有一些点,然后这个时候要选定一个长宽固定的框,使得这个框内的点数最多。
那么首先可以确定的就是以某一个点作为这个框的左下端点一定包含最优解。
假设以 i i i作为左端点,那么这个框能够覆盖的时间就是 [ X i , X i + D ) [X_i, X_i + D) [Xi,Xi+D),长度就是 [ T i , T i + W ) [T_i, T_i + W) [Ti,Ti+W),所以遇到 X i X_i Xi时我们就给长度区间+1,遇到 X i + D X_i+D Xi+D时就给长度区间-1,答案就是每次取全局最大值。
那么我们就可以枚举时间,以位置建线段树,执行区间加和区间查询的操作。
时间复杂度 O ( N l o g ( 2 E 5 ) ) O(Nlog(2E5)) O(Nlog(2E5))
#include <bits/stdc++.h>
using i64 = long long;
constexpr int inf = 1E9;
struct Tag {
int add = inf;
void apply(const Tag& t) {
if (t.add != inf) {
if (add == inf) {
add = 0;
}
add += t.add;
}
}
};
struct Info {
int max = 0;
void apply(const Tag& t) {
if (t.add != inf) {
max += t.add;
}
}
};
Info operator+(Info a, Info b) {
Info c;
c.max = std::max(a.max, b.max);
return c;
}
constexpr int C = 1 << 18;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, D, W;
std::cin >> N >> D >> W;
std::vector<std::vector<std::pair<int, int>>> info(C);
for (int i = 0; i < N; i++) {
int T, X;
std::cin >> T >> X;
info[T].push_back({X, 1});
if (T + D < C) {
info[T + D].push_back({X, -1});
}
}
int ans = 0;
LazySegmentTree<Info, Tag> seg(C);
for (int x = 0; x < C; x++) {
for (auto [y, v] : info[x]) {
seg.rangeApply(y, std::min(C, y + W), {v});
}
ans = std::max(ans, seg.rangeQuery(0, C).max);
}
std::cout << ans << "\n";
return 0;
}