Atcoder Beginner Contest 395
比赛链接:Atcoder Beginner Contest 395
Github 链接:ABC395
A - Strictly Increasing?
按照题意模拟。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
bool flag = true;
for (int i = 1; i < n; i++)
if (a[i] <= a[i - 1]) {
flag = false;
break;
}
if (flag) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
B - Make Target
按照题意模拟。
#include <bits/stdc++.h>
using namespace std;
char s[55][55];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
int j = n - i + 1;
if (i <= j) {
char c;
if (i & 1) c = '#';
else c = '.';
for (int x = i; x <= j; x++)
for (int y = i; y <= j; y++) s[x][y] = c;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << s[i][j];
}
cout << endl;
}
return 0;
}
C - Shortest Duplicate Subarray
对于每一个相同的 \(a\) 的值,记录他们的下标,答案是相距最近的两个相同的 \(a\) 值的距离
时间复杂度:\(O(N + A)\)(\(A\) 代表 \(A_i\) 的最大值)。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, a[N], maxm = 0;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
maxm = max(a[i], maxm);
}
vector<vector<int>> vis(maxm + 1);
for (int i = 1; i <= n; i++) vis[a[i]].push_back(i);
int ans = INT_MAX;
for (int i = 1; i <= maxm; i++) {
if (vis[i].size() >= 2) {
for (int j = 1; j < vis[i].size(); j++)
ans = min(ans, vis[i][j] - vis[i][j - 1] + 1);
}
}
if (ans != INT_MAX) cout << ans << endl;
else cout << -1 << endl;
return 0;
}
D - Pigeon Swap
- 对于第一种操作,只需要对每一个鸽子记录一个 \(f_i\),表示第 \(i\) 只鸽子所在的鸟巢编号。
- 对于第二种操作,
- 要将所有 \(f_i = a\) 的位置,变成 \(f_i = b\)。
- 同时将所有 \(f_i = b\) 的位置,变成 \(f_i = a\)。
如果暴力的做,那么时间复杂度是 \(O(n)\),无法满足题目限制。
- 可以发现 \(f_i\) 的值跟最终的鸟巢编号存在一个映射的关系,所以记录一个 \(pos_i\) 来表示这个映射关系。初始化 \(pos_i = i\),表示最开始每一个 \(f_i\) 的值都表示对应的鸟巢。对于第二种操作,只需要更改对应的映射关系即可,即交换 \(pos_a\) 跟 \(pos_b\) 的值。
- 但此事第一个操作就不能直接赋值 \(f_i = b\) 了,而是要让 \(f_i = j\)(\(pos_j = b\)),所以要另外再记一个 \(idx_i\) 作为跟 \(pos_i\) 刚好相反的一个映射,方便进行操作一。
在这样的情况下,
- 操作一实际上就是让 \(f_a = idx_b\)。
- 操作二实际上是要分别交换 (\(pos_{idx_a}\), \(pos_{idx_b}\)),跟(\(pos_a\), \(pos_b\))。
- 操作三实际上是要输出 \(pos_{f_a}\)。
时间复杂度:\(O(N + Q)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, q, f[N], pos[N], idx[N];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> q;
for (int i = 1; i <= n; i++) f[i] = pos[i] = idx[i] = i;
while (q--) {
int op, a, b;
cin >> op;
if (op == 1) {
cin >> a >> b;
f[a] = idx[b];
}
if (op == 2) {
cin >> a >> b;
swap(pos[idx[a]], pos[idx[b]]);
swap(idx[a], idx[b]);
}
if (op == 3) {
cin >> a;
cout << pos[f[a]] << '\n';
}
}
return 0;
}
E - Flip Edge
对于每一对给出的 \((u, v)\),直接连一条有向边,权值为 \(1\)。
为了表示翻转操作,可以给 \((v + n, u + n)\) 连一条边,权值为 \(1\),并且对于 \(1 \le i \le n\),连 \((i, i + n)\) 跟 \((i + n, i)\) 两条边,权值都设为 \(x\)。
接着,直接从 \(1\) 号节点开始跑 \(dijsktra\),最后的答案为 \(\min(dis_n, dis_{2n})\)。
时间复杂度:\(O(n\log n)\)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10, M = N << 3;
int n, m, x, head[N << 1], num = 0, dis[N << 1], vis[N << 1] = { 0 };
struct edge { int to, nxt, w; } e[M];
struct node {
int u, d;
bool operator< (const node &curNode) const {
return curNode.d < d;
}
};
void addEdge(int u, int v, int w) { e[++num] = (edge){ v, head[u], w}, head[u] = num; }
void dijsktra(int s) {
for (int i = 1; i <= 2 * n; i++) dis[i] = LLONG_MAX;
dis[s] = 0;
priority_queue<node> q;
q.push({s, 0});
while (!q.empty()) {
int u = q.top().u; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to, w = e[i].w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({v, dis[v]});
}
}
}
}
signed main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m >> x;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
addEdge(u, v, 1), addEdge(v + n, u + n, 1);
}
for (int i = 1; i <= n; i++) addEdge(i, i + n, x), addEdge(i + n, i, x);
dijsktra(1);
cout << min(dis[n], dis[2 * n]) << endl;
return 0;
}
F - Smooth Occlusion
可以发现 \(H\) 满足单调性(\(H\) 越大花费越少),我们可以二分 \(H\),然后 \(O(n)\) 检查是不是符合条件。
检查实现:
- 对于每一个位置 \(i\),需要进行的消除次数是 \(U_i + D_i - H\)。
- 为了满足 \(|U_i - U_{i + 1}| \le X\) 的条件,\(U_{i + 1}\) 可行的范围是 \([\min U_i - X, \max U_i + X]\)。
- \(\min U_i = \max(0, U_i - (U_i + D_i - H)) = \max (0, H - D_i)\) , \(\max U_i = U_i\)。
时间复杂度:\(O(N \log(U_i + D_i))\)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, x, u[N], d[N];
bool check(int mid) {
int l = LLONG_MIN, r = LLONG_MAX;
for (int i = 1; i <= n; i++) {
l = max(l, max(0LL, mid - d[i])), r = min(r, u[i]);
if (l > r) return false;
l -= x, r += x;
}
return true;
}
signed main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> x;
int l = 0LL, r = LLONG_MAX, tar = 0LL;
for (int i = 1; i <= n; i++) cin >> u[i] >> d[i], r = min(r, u[i] + d[i]);
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) l = mid + 1, tar = mid;
else r = mid - 1;
}
int ans = 0LL;
for (int i = 1; i <= n; i++) ans += u[i] + d[i] - tar;
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号