A. Minimum Binary Number
题意:给你一个01串,你每次可以交换相邻的两个元素,或者把两个相邻的1变成一个1。求二进制表示小最小的数。
显然我们可以把1消除到只剩一个。那么答案就是一个1加原串的所有0.
要特判原串只有一个0的情况。
点击查看代码
void solve() {int n;std::cin >> n;std::string s;std::cin >> s;if (n == 1) {std::cout << s << "\n";return;}int cnt = std::ranges::count(s, '0');std::string ans = "1" + std::string(cnt, '0');std::cout << ans << "\n";
}
B. Lara Croft and the New Game
题意:\(n\times m\)的矩阵,起点为\((1, 1)\)。按如下规则移动,先移动到\(n, 1\),然后移动到\(n, m\),之后按\(S\)型移动,也就是先移动到\(n - 1, m\),然后向左到\(n - 1, 2\),然后移动到\(n - 2, 2\),然后向右移动到\(n - 2, m\)。如此左右切换。移动一格算一步,求移动\(k\)步后到了哪里。
分两部分判断,一部分是还没有进行\(S\)型移动,也就是\(k \leq n + m - 2\)。
否则求出移动了多少行,也就是\(\lceil \frac{k - (n + m - 2)}{m - 1} \rceil\),根据奇偶性得到接下来向左还是向右,然后剩下的步数就是\((k - (n + m - 2)) \% (m - 1)\)。
点击查看代码
void solve() {i64 n, m, k;std::cin >> n >> m >> k;if (k <= n + m - 2) {if (k <= n - 1) {std::cout << 1 + k << " " << 1 << "\n";} else {std::cout << n << " " << k - n + 2 << "\n";}return;}k -= n + m - 2;int r = (k + m - 2) / (m - 1), c = k % (m - 1) == 0 ? m - 1 : k % (m - 1);if (r & 1) {std::cout << n - r << " " << m - c + 1 << "\n";} else {std::cout << n - r << " " << 1 + c << "\n";}
}
C. Nested Segments
题意:给你\(n\)个区间,求两个区间使得一个区间被另一个区间完全包含。
先按右端点排序,那么我们从前往后枚举,前面的区间的右端点都比当前端点小。那么我们只需要中一个左端点比当前大的进行,可以用\(set\)存前面的左端点,然后二分。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<std::array<int, 3>> a(n);for (int i = 0; i < n; ++ i) {int l, r;std::cin >> l >> r;a[i] = {r, l, i};}std::ranges::sort(a, [&](std::array<int, 3> & a, std::array<int, 3> & b) {if (a[0] == b[0]) {return a[1] > b[1];}return a[0] < b[0];});std::set<std::pair<int, int>> s;for (auto & [r, l, id] : a) {auto it = s.lower_bound({l, 0});if (it != s.end()) {std::cout << it->second + 1 << " " << id + 1 << "\n";return;}s.insert({l, id});}std::cout << -1 << " " << -1 << "\n";
}
D. Degree Set
题意:给你一个递增序列\(d\),你要构造一个有\(d_n + 1\)个点的图,使得这些点的度数集合是\(d\)。
对于一个\(n\)个点的图,如果我们给\([1, i]\)的点给其它点都连边,那么\([1, i]\)的点的度数都是\(n - 1\),其它点的度数是\(i\)。
那么我们可以先给\([1, d_1]\)向\([1, d_n]\)其它点都连边,然后给\([d_1 + 1, d_2]\)的点\([d_1 + 1, d_{n-1}+1]\)的点都连边,给\([d_{i-1} + 1, d_i]\)向\([d_{i} + 1, d_{n-i+1}+1]\)的连边,就正好满足条件。因为第\(i\)部分的每个点有\(n - d_{n-i+1} - 1\)个没和它连边,度数恰好是\(d{n-i+1}\)。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<int> a(n + 1);for (int i = 1; i <= n; ++ i) {std::cin >> a[i];}int l = 1, r = n;std::vector<std::pair<int, int>> ans;while (l <= r) {for (int i = a[l - 1] + 1; i <= a[l]; ++ i) {for (int j = i + 1; j <= a[r] + 1; ++ j) {ans.emplace_back(i, j);}}++ l;-- r;}std::cout << ans.size() << "\n";for (auto & [u, v] : ans) {std::cout << u << " " << v << "\n";}
}
E. Well played!
题意:有\(n\)个人,每个有生命值和攻击力,你可以进行\(a\)次操作把某个人的生命值乘二,可以进行\(b\)次操作把攻击力的值变成生命值的值。问最终攻击力最大和。
我们把第一类操作都给一个人值最优的。因为如果一个人生命值乘二然后赋值给攻击力更优,那么下一次再给他乘二,它也是最优的。
那么我们枚举第一类操作给谁,然后选\(k-1\)个人把生命值赋值给攻击力,这个可以用\(set\)维护。
点击查看代码
void solve() {int n, m, k;std::cin >> n >> m >> k;k = std::min(n, k);i64 sum = 0;std::vector<std::array<int, 3>> a(n);for (int i = 0; i < n; ++ i) {int h, d;std::cin >> h >> d;a[i] = {h - d, h, d};sum += d;}if (k == 0) {std::cout << sum << "\n";return;} std::ranges::sort(a, std::greater<>());std::multiset<int> s;for (int i = 0; i < k; ++ i) {if (a[i][0] > 0) {s.insert(a[i][0]);sum += a[i][0];}}i64 ans = sum;for (int i = 0; i < n; ++ i) {if (a[i][0] > 0 && i < k) {s.extract(a[i][0]);sum -= a[i][0];}i64 v = (1ll << m) * a[i][1] - a[i][2];if (s.size() == k) {ans = std::max(ans, sum - *s.begin() + v);} else {ans = std::max(ans, sum + v);}if (a[i][0] > 0 && i < k) {s.insert(a[i][0]);sum += a[i][0];} }std::cout << ans << "\n";
}