A. Circle of Apple Trees
题意:一个环形数组,每次到一个位置可以选择拿走这个元素或者跳过,然后走到下一个位置。每次拿的数要比之前拿的大,求最多拿多少数。
显然可以从小到大拿,那么答案就是不同数的个数。
点击查看代码
#include <bits/stdc++.h>using i64 = long long;void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}std::set<int> s(a.begin(), a.end());std::cout << s.size() << "\n";
}int main() {std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);int t = 1;std::cin >> t;while (t -- ) {solve();}return 0;
}
B. Bitwise Reversion
题意:给你\(x, y, z\),求有没有\(a, b, c\)满足\(a \& b = a, b \& c = y, a\& c = z\)。
那么如果\(x, y, z\)中有一位是两个数有另一个数没有的,无解。
例如\(x, y\)第\(k\)位是\(1\),\(z\)第\(k\)位是\(0\),那么发现\(a, b, c\)的第\(k\)位都是\(1\)。则\(a \& c\)第\(k\)位一定是\(1\)。
点击查看代码
#include <bits/stdc++.h>using i64 = long long;void solve() {int a[3]{};for (int i = 0; i < 3; ++ i) {std::cin >> a[i];}for (int i = 0; i < 30; ++ i) {int cnt = (a[0] >> i & 1) + (a[1] >> i & 1) + (a[2] >> i & 1);if (cnt == 2) {std::cout << "NO\n";return;}}std::cout << "YES\n";
}int main() {std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);int t = 1;std::cin >> t;while (t -- ) {solve();}return 0;
}
C. Symmetrical Polygons
题意:给你\(n\)条长度为\(a_i\)的木棒,求用它们一些组成的对称凸多边形的最大周长是多少。
如果一个长度出现偶数次,我们就可以把它们都拿走。对于出现奇数次的长度,留一个不拿。
那么在拿了至少两个木棒的情况下,最多拿两个留下的木棒。拿一个的情况就是如果已有总长度大于当前木棒长度这个木棒就可以拿,拿两个的情况就是当前总长度加短的那个木棒的长度大于长的木棒长度就可以拿,枚举长的那个木棒的长度,那么短的肯定选比它短的里最长的最优。
点击查看代码
#include <bits/stdc++.h>using i64 = long long;void solve() {int n;std::cin >> n;std::vector<i64> a(n);std::map<i64, i64> mp;for (int i = 0; i < n; ++ i) {std::cin >> a[i];++ mp[a[i]];}i64 sum = 0;std::vector<i64> b;i64 cnt = 0;i64 len = 0;for (auto & [x, y] : mp) {if (y % 2 == 0) {sum += y * x;cnt += y;} else {sum += (y - 1) * x;cnt += (y - 1);b.push_back(x);}}i64 ans = 0;if (cnt >= 3) {ans = sum;}if (cnt == 0) {std::cout << 0 << "\n";return;}std::ranges::sort(b, std::greater<>());for (auto & x : b) {if (sum > x) {ans = std::max(ans, sum + x);}}for (int i = 1; i < b.size(); ++ i) {if (sum + b[i] > b[i - 1]) {ans = std::max(ans, sum + b[i] + b[i - 1]);}}std::cout << ans << "\n";
}int main() {std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);int t = 1;std::cin >> t;while (t -- ) {solve();}return 0;
}
D. Not Alone
题意:一个环形数组,你每次可以把一个数加一或减一,求使得每个位置至少有一个相邻的数和它相同的最小操作数。
结论是把每个相同段长度为\(2\)或者\(3\)是最优的。长度为\(2\)的代价为\(|a_i - a_{i+1}|\),长度为\(3\)的代价为\(\max\{a_{i-1}, a_i, a_{i+1}\} - \min\{a_{i-1}, a_i, a_{i+1}\}\)。
证明就是,对于长度为\(m \geq 4\)的相同块,记为\(b_1, b_2, ... , b_m\),最后都变成\(x\),那么代价为\(\sum_{i=1}^{m} |b_i - x|\),拿出后两个来,代价为\((\sum_{i=1}^{m-2} |b_i - x|) + |b_{m-1} - b_{m}|\)。发现只有\(x = b_{m-1}\)或\(x = b_{m}\)时才有\(|b_{m-1} - x| + |b_{m} - x| = |b_{m-1} - b_{m}|\),其它情况都是大于\(|b_{m-1} - b_{m}|\)。所以把最后两个分出来代价只会不变或减少。同理分出来三个也是一样的。
那么如果不是环形数组的话,考虑\(dp\),有\(f[i] -> f[i + 2] + |a_{i+1} - a_{i+2}|\),\(f[i] -> f[i + 3] + max\{a_{i+1}, a_{i+2}, a_{i+3}\} - \min\{a_{i+1}, a_{i+2}, a_{i+3}\}\)。
现在考虑环形,那么只需要考虑\(a_1, a_n\)相同,\(a_1, a_{n}, a_{n-1}\)相同,\(a_1, a_2, a_n\)相同的情况。那么其它部分是连续的,可以看作一个一维数组去\(dp\)。
点击查看代码
#include <bits/stdc++.h>using i64 = long long;void solve() {int n;std::cin >> n;std::vector<i64> a(n + 2);for (int i = 1; i <= n; ++ i) {std::cin >> a[i];}a[n + 1] = a[1];a[0] = a[n];std::vector<i64> c1(n + 1), c2(n + 1);for (int i = 1; i <= n; ++ i) {c1[i] = std::abs(a[i] - a[i + 1]);i64 max = std::max({a[i], a[i - 1], a[i + 1]});i64 min = std::min({a[i], a[i - 1], a[i + 1]});c2[i] = max - min;}constexpr i64 inf = 1e18;auto dp = [&](int l, int r) -> i64 {std::vector<i64> f(n + 1, inf);f[l - 1] = 0;for (int i = l - 1; i + 2 <= r; ++ i) {f[i + 2] = std::min(f[i + 2], f[i] + c1[i + 1]); if (i + 3 <= r) {f[i + 3] = std::min(f[i + 3], f[i] + c2[i + 2]);}}return f[r];};i64 ans = dp(1, n);if (n > 3) {ans = std::min(ans, c1[n] + dp(2, n - 1));}if (n > 4) {ans = std::min(ans, c2[n] + dp(2, n - 2));ans = std::min(ans, c2[1] + dp(3, n - 1));}std::cout << ans << "\n";
}int main() {std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);int t = 1;std::cin >> t;while (t -- ) {solve();}return 0;
}