AtCoder Beginner Contest 216 【A - G】
比赛链接:https://atcoder.jp/contests/abc216/tasks
A - Signed Difficulty
题意
给出一个 x.y
形式的实数,按以下格式转换:
- \(0 \le y \le 2\) ,输出
x-
- \(3 \le y \le 6\) ,输出
x
- \(7 \le y \le 9\) ,输出
x+
题解
模拟。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int x, y;
scanf("%d.%d", &x, &y);
printf("%d%s\n", x, y <= 2 ? "-" : (y <= 6 ? "" : "+"));
return 0;
}
B - Same Name
题意
给出 \(n\) 个人的姓、名,如果存在重名输出 Yes
否则输出 No
。
题解
模拟。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
map<pair<string, string>, int> mp;
for (int i = 0; i < n; i++) {
string s, t;
cin >> s >> t;
if (++mp[{s, t}] == 2) {
cout << "Yes" << "\n";
return 0;
}
}
cout << "No" << "\n";
return 0;
}
C - Many Balls
题意
一开始 \(x = 0\) ,每次可以执行以下两种操作之一:
- \(x = x + 1\)
- \(x = 2x\)
输出一种将 \(x\) 转换为 \(n\) 的操作方案。
题解
根据奇偶性逆推即可。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n;
cin >> n;
string ans;
while (n != 0) {
if (n % 2 == 0) {
ans += 'B';
n /= 2;
} else {
ans += "A";
n -= 1;
}
}
reverse(ans.begin(), ans.end());
cout << ans << "\n";
return 0;
}
D - Pair of Balls
题意
有 \(n\) 种颜色,每个颜色的球有 \(2\) 个,这 \(2n\) 个球共排 \(m\) 行,每行有 \(k_i\) 个。
给出这些球的颜色分布情况,每次可以拿下不同行首部两个颜色相同的球,问能否拿完所有球。
题解
本质上还是一个模拟,不过拿的过程是递归的。
保存一下每行取到哪一列,每种颜色第一次出现的球在哪一行,遍历一次第一列即可。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<int>> a(m);
for (int i = 0; i < m; i++) {
int k;
cin >> k;
a[i].resize(k);
for (int j = 0; j < k; j++) {
cin >> a[i][j];
--a[i][j];
}
}
vector<int> p(m, 0), mp(n, -1);
function<void(int, int)> dfs = [&](int i, int j) {
if (j == (int)a[i].size()) {
return;
}
if (mp[a[i][j]] != -1) {
++p[mp[a[i][j]]], ++p[i];
dfs(mp[a[i][j]], p[mp[a[i][j]]]);
dfs(i, p[i]);
} else {
mp[a[i][j]] = i;
}
};
for (int i = 0; i < m; i++) {
dfs(i, 0);
}
bool ok = true;
for (int i = 0; i < m; i++) {
if (p[i] != (int)a[i].size()) {
ok = false;
}
}
cout << (ok ? "Yes" : "No") << "\n";
return 0;
}
E - Amusement Park
题意
有 \(n\) 个数,初始值为 \(a_i\) ,每次可以加上某个 \(a_i\) ,然后该 \(a_i\) 减一。
最多可以加 \(k\) 次,问可以加得的总和最大为多少。
题解
要打比方的话,就是把一个升序直方图相同的值从大到小成段地削到与前一个数相平。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
#define int long long
int n, k;
cin >> n >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a.begin(), a.end());
int l = n, r = n;
auto cal = [&](int a, int b, int len) {
return (b - a + 1) * (a + b) / 2 * len;
};
long long ans = 0;
while (l > 0) {
int len = r - l + 1;
int sum = cal(a[l - 1] + 1, a[l], len);
if (k <= len * (a[l] - a[l - 1])) {
int x = k / len;
ans += cal(a[l] - x + 1, a[l], len) + k % len * (a[l] - x);
k = 0;
break;
}
k -= len * (a[l] - a[l - 1]);
ans += sum;
--l;
}
cout << ans << "\n";
return 0;
}
F - Max Sum Counting
题意
给出两个大小均为 \(n\) 的数组 \(a, b\) ,问 \(\{1,2,\dots,n\}\) 有多少非空子集 \(s\) 使得:
- \(max_{i \in s}a_i \ge \sum _{i \in s} b_i\)
题解
将 \(a_i, b_i\) 绑定后按 \(a_i\) 升序排列,这样每次遍历时 \(max_{i \in s}a_i\) 就已经确定了,只需计算之前有多少子序列加上 \(b_i\) 小于 \(a_i\) 即可。
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MOD = 998244353;
int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }
struct Z {
int x;
Z(int x = 0) : x(norm(x)) {}
int val() const { return x; }
Z operator-() const { return Z(norm(MOD - x)); }
Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<pair<int, int>> v(n);
for (auto& [x, y] : v) {
cin >> x;
}
for (auto& [x, y] : v) {
cin >> y;
}
sort(v.begin(), v.end());
vector<int> a(n), b(n);
for (int i = 0; i < n; i++) {
a[i] = v[i].first;
b[i] = v[i].second;
}
Z ans = 0;
vector<Z> dp(a.back() + 1);
dp[0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j + b[i] <= a[i]; j++) {
ans += dp[j];
}
for (int j = a.back(); j >= b[i]; j--) {
dp[j] += dp[j - b[i]];
}
}
cout << ans.val() << "\n";
return 0;
}
G - 01Sequence
题意
构造一个长为 \(n\) 的 01 串,使得每个 \([l_i, r_i]\) 中至少有 \(x_i\) 个 1 ,且总的 1 的个数最少。
题解
贪心,将区间按 \(r_i\) 排序,当 \(\sum \limits _{i = l_i}^{r_i} s_i \lt x_i\) 时,将 1 尽可能地靠右放,区间求和以及查询、更新空余位置可以分别用 Fenwick_tree
和 set
。
代码
#include <bits/stdc++.h>
using namespace std;
struct Fenwick_tree {
vector<int> bit;
Fenwick_tree(int n) : bit(n + 1) {}
void update(int pos, int val) {
for (int i = pos; i < (int)bit.size(); i += i & (-i)) {
bit[i] += val;
}
}
int query(int l, int r) {
return query(r) - query(l - 1);
}
int query(int pos) {
int res = 0;
for (int i = pos; i >= 1; i -= i & (-i)) {
res += bit[i];
}
return res;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<tuple<int, int, int>> v(m);
for (auto& [r, l, x] : v) {
cin >> l >> r >> x;
}
sort(v.begin(), v.end());
Fenwick_tree F(n);
set<int> st;
for (int i = 1; i <= n; i++) {
st.insert(i);
}
vector<int> ans(n + 1);
for (auto [r, l, x] : v) {
while (F.query(l, r) < x) {
auto it = prev(st.upper_bound(r));
ans[*it] = 1;
F.update(*it, 1);
st.erase(it);
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " \n"[i == n];
}
return 0;
}