2024-07-31-ABC364
A - Glutton Takahashi
有N道菜,高桥按顺序依次吃,每道菜是sweet或者salty,如果他连续吃两道甜的,他会感到不舒服,不能再吃别的了,请你计算他能不能吃掉所有的菜。
模拟即可,依次判断一下sweet之前是不是sweet
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<std::string> S(N);
for (int i = 0; i < N; i++) {
std::cin >> S[i];
}
for (int i = 1; i < N - 1; i++) {
if (S[i] == "sweet" && S[i - 1] == S[i]) {
std::cout << "No\n";
return 0;
}
}
std::cout << "Yes\n";
return 0;
}
B - Grid Walk
给定一个二维网格,有障碍物,有空地,给定初始位置。
给定移动操作,若移动目标位置为空地则移动,否则留在原地。
问最终位置。
模拟即可
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int H, W;
std::cin >> H >> W;
int X, Y;
std::cin >> X >> Y;
X--;
Y--;
std::vector<std::string> C(H);
for (int i = 0; i < H; i++) {
std::cin >> C[i];
}
std::string S;
std::cin >> S;
for (auto c : S) {
int nx = X;
int ny = Y;
if (c == 'L') {
ny--;
} else if (c == 'R') {
ny++;
} else if (c == 'U') {
nx--;
} else {
nx++;
}
if (0 <= nx && nx < H && 0 <= ny && ny < W && C[nx][ny] == '.') {
X = nx;
Y = ny;
}
}
std::cout << X + 1 << " " << Y + 1 << "\n";
return 0;
}
C - Minimum Glutton
有N道菜,第i道菜有甜度Ai和咸度Bi,高桥要安排吃菜的顺序,他会在吃过的甜度大于X或者咸度大于Y时停下,请问他最少吃多少菜才会停止。
满足一个条件即可,所以我们试一遍最大的甜度再试一遍最大的咸度即可。
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
i64 X, Y;
std::cin >> N >> X >> Y;
std::vector<int> A(N), B(N);
for (int i = 0; i < N; i++) {
std::cin >> A[i];
}
for (int i = 0; i < N; i++) {
std::cin >> B[i];
}
int ans = N;
std::sort(A.begin(), A.end(), std::greater<>());
std::sort(B.begin(), B.end(), std::greater<>());
for (int i = 0; i < N; i++) {
X -= A[i];
if (X < 0) {
ans = std::min(ans, i + 1);
}
}
for (int i = 0; i < N; i++) {
Y -= B[i];
if (Y < 0) {
ans = std::min(ans, i + 1);
}
}
std::cout << ans << "\n";
return 0;
}
D - K-th Nearest
一维坐标,给定n个点,依次回答q个询问。
每个询问给定一个位置,问距离该位置第k近的点的距离是多少。
二分答案距离,用upper和lower检查距离内点的数量。
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, Q;
std::cin >> N >> Q;
std::vector<int> a(N), b(Q), k(Q);
for (int i = 0; i < N; i++) {
std::cin >> a[i];
}
for (int i = 0; i < Q; i++) {
std::cin >> b[i] >> k[i];
}
std::sort(a.begin(), a.end());
for (int i = 0; i < Q; i++) {
int lo = 0, hi = 2E8;
while (lo < hi) {
int x = (lo + hi) / 2;
int l = b[i] - x;
int r = b[i] + x;
int cnt = std::upper_bound(a.begin(), a.end(), r) - std::lower_bound(a.begin(), a.end(), l);
if (cnt >= k[i]) {
hi = x;
} else {
lo = x + 1;
}
}
std::cout << lo << "\n";
}
return 0;
}
E - Maximum Glutton
有N道菜,第i道菜有甜度Ai和咸度Bi,高桥要安排吃菜的顺序,他会在吃过的甜度大于X或者咸度大于Y时停下,请问他最多吃多少菜才会停止。
顺序其实无所谓,就是问最多吃几道菜。考虑\(dp_{i,j}\)表示吃了i道菜,甜度为j时的最小的咸度,每次考虑一道新菜吃或不吃去更新一下dp,然后最后O(NX)统计一下答案即可,总时间复杂度O(NNX).
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N, X, Y;
std::cin >> N >> X >> Y;
std::vector dp(N + 1, std::vector<int>(X + 1, Y + 1));
dp[0][0] = 0;
for (int i = 0; i < N; i++) {
int A, B;
std::cin >> A >> B;
for (int c = i; c >= 0; c--) {
for (int d = X; d >= A; d--) {
dp[c + 1][d] = std::min(dp[c + 1][d], dp[c][d - A] + B);
}
}
}
int ans = 0;
for (int i = 0; i < N; i++) {
for (int x = 0; x <= X; x++) {
if (dp[i][x] <= Y) {
ans = std::max(ans, i + 1);
}
}
}
std::cout << ans << "\n";
return 0;
}
F - Range Connect MST
一共有N+Q个点,下标为1到N+Q,一共有Q此操作,第i次操作给定L,R,C,将编号在LR之间的点都连到N+i号点上,边权为C,所有操作完成后,问图是否连通,如果连通请输出最小生成树。
我们思考如何构建最小生成树:
考虑克鲁斯卡尔,我们将所有的操作按C排序,然后如果LR之间的j点和N+i不在一个连通块内,那么就连上,这是一个显然正确的最朴素的最小生成树解法,
但是容易看出的是,如果我们跑L到R的话,时间复杂度为O(NQlogN),是通过不了的。
聪明的优化方法:首先后Q个点其实没什么用,我们还是在将每一段点相互之间连接成一个集合,并且每一次链接的代价是C,最后前N个点要连成一个集合的话,后Q个其实一定被连上了。那么我们现在转移思路,我们顺序考虑L到R之间每两个相邻的点是否在一个集合内,这样的话时间复杂度其实还是O(NQlogN)。
小技巧:在做并查集的合并操作时,总将较大编号的点作为根,那么我们在循环的时候可以用下面的操作进行编号的跳跃:
for (int x = dsu.find(L[i]); x < R[i] - 1; x = dsu.find(x)) {
dsu.merge(x + 1, x);
ans += C[i];
}
那么此时,LR中在同一个集合里的j就会被跳过,只会合并没有链接的集合,时间复杂度就下降到了O(NlogN)。
#include <bits/stdc++.h>
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(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) {
return find(x) == find(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, Q;
std::cin >> N >> Q;
std::vector<int> L(Q), R(Q), C(Q);
for (int i = 0; i < Q; i++) {
std::cin >> L[i] >> R[i] >> C[i];
L[i]--;
}
std::vector<int> p(Q);
std::iota(p.begin(), p.end(), 0);
std::sort(p.begin(), p.end(),
[&](int i, int j) {
return C[i] < C[j];
});
DSU dsu(N);
i64 ans = 0;
for (auto i : p) {
ans += C[i];
for (int x = dsu.find(L[i]); x < R[i] - 1; x = dsu.find(x)) {
dsu.merge(x + 1, x);
ans += C[i];
}
}
if (dsu.find(0) != N - 1) {
std::cout << -1 << "\n";
} else {
std::cout << ans << "\n";
}
return 0;
}