VP Educational Codeforces Round 76 (Rated for Div. 2)
A. Two Rival Students
题意:\(n\)个位置,可以操作\(x\)次,有两个人在\(a, b\)点。你每次使得一个人往左或往右移动一步。求最大距离。
应该让两个人分别往两边走。
点击查看代码
void solve() {
int n, x, a, b;
std::cin >> n >> x >> a >> b;
if (a > b) {
std::swap(a, b);
}
int l = a - 1, r = n - b;
int ans = b - a + std::min(x, l + r);
std::cout << ans << "\n";
}
B. Magic Stick
题意:一个数\(a\)如果是偶数可以变成\(\frac{3a}{2}\),如果大于\(1\)也可以减一。求\(x\)能不能变成\(y\)。
观察到第一个运算是让\(a = a + \frac{a}{2}\)。那么可以一直加,如果是奇数就加一然后继续加。发现除了\(2\)最多变成\(3\)外,其它数都可以一直变大。那么只要\(x \geq y\)或者\(x == 2 \&\& y \leq 3\)或\(x > 3\)就可以。
点击查看代码
void solve() {
int x, y;
std::cin >> x >> y;
if (x >= y || (x == 2 && y == 3) || x >= 4) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
C. Dominated Subarray
题意:求\(a\)最短的好子数组。一个子数组是好的,那么它的长度大于等于2且有一个数出现了超过一半的次数。
一个结论是最短的子数组一定是以相近的两个相同数为两端。如果中间数出现次数都是一次,则显然好的。否则它中间有数出现次数大于等于2,那么可以缩小范围,同样讨论,如果不满足则在缩小,最终一定满足条件。那么选最近的相同的数即可。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
-- a[i];
}
std::vector<int> last(n, -1);
int ans = -1;
for (int i = 0; i < n; ++ i) {
if (last[a[i]] != -1) {
if (ans == -1 || ans > i - last[a[i]] + 1) {
ans = i - last[a[i]] + 1;
}
}
last[a[i]] = i;
}
std::cout << ans << "\n";
}
D. Yet Another Monster Killing Problem
题意:\(n\)个怪物,每个怪物有\(a_i\)战斗力。\(m\)个英雄,每个英雄有\(m_i\)战斗里和\(p_i\)体力。\(n\)个怪物需要依次挑战。每次选择一个英雄,每个英雄可以选择任意次。一个英雄会一直打败战斗力小于等于它的怪物,直到没有怪物或者怪物战斗力大于等于它,同时英雄一次最多打败\(p_i\)个怪物。求打败所有怪物需要派几次英雄。
对于怪物\(i\),我们应该派出\(m_i \geq a_i\)的英雄里\(p_i\)最大的。然后看可以一直打到哪只怪物,不断更新\(p\)直到结束。那么按\(m\)排序后,预处理\(p\)的后缀最大值。一直模拟即可。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int m;
std::cin >> m;
std::vector<std::pair<int, int>> b(m);
for (int i = 0; i < m; ++ i) {
std::cin >> b[i].first >> b[i].second;
}
std::ranges::sort(b);
for (int i = m - 2; i >= 0; -- i) {
b[i].second = std::max(b[i].second, b[i + 1].second);
}
int ans = 0;
for (int i = 0; i < n; ++ i) {
int p = std::ranges::lower_bound(b, std::make_pair(a[i], 0)) - b.begin();
if (p == m) {
std::cout << -1 << "\n";
return;
}
++ ans;
int j = i;
while (j + 1 < n) {
while (p < m && b[p].first < a[j + 1]) {
++ p;
}
if (p == m || j + 1 - i + 1 > b[p].second) {
break;
}
++ j;
}
i = j;
}
std::cout << ans << "\n";
}
E. The Contest
题意:\(n\)个数分给了三个数组,你需要选择\(i, j\),那么需要第一个数组拥有\([1, i]\)的所有数,第二个数组拥有\([i + 1, j]\)的所有数,第三个数组拥有\([j + 1, n]\)的所有数。每次操作可以把一个数移动到另一个数组。求最少操作数。
给每个数组开一个前缀和\(sum_i\)表示前\(i\)个数有几个数出现在这个数组里。分别记为\(sum1, sum2, sum3\)。
那么对于\(i, j\)来说,就是\(sum1[n] - sum1[i] + sum2[i] - sum2[n] - sum2[j] + sum3[j]\)。发现\(i\)和\(j\)是分开的。那么可以预处理\(sum3[j] - sum2[j]\)的最小后缀值。枚举\(i\)。
点击查看代码
void solve() {
int k1, k2, k3;
std::cin >> k1 >> k2 >> k3;
int n = k1 + k2 + k3;
std::vector<int> sum1(n + 1), sum2(n + 1), sum3(n + 1);
for (int i = 1; i <= k1; ++ i) {
int x;
std::cin >> x;
sum1[x] = 1;
}
for (int i = 1; i <= k2; ++ i) {
int x;
std::cin >> x;
sum2[x] = 1;
}
for (int i = 1; i <= k3; ++ i) {
int x;
std::cin >> x;
sum3[x] = 1;
}
for (int i = 1; i <= n; ++ i) {
sum1[i] += sum1[i - 1];
sum2[i] += sum2[i - 1];
sum3[i] += sum3[i - 1];
}
//[1, i],[i + 1, j], [j + 1, n] => sum1[n] - sum1[i] + sum2[i] + sum2[n] - sum2[j] + sum3[j]
std::vector<int> suf(n + 2);
suf[n + 1] = 1e9;
for (int i = n; i >= 0; -- i) {
suf[i] = std::min(sum3[i] - sum2[i], suf[i + 1]);
}
int ans = 1e9;
for (int i = 0; i <= n; ++ i) {
ans = std::min(ans, sum1[n] - sum1[i] + sum2[i] + sum2[n] + suf[i]);
}
std::cout << ans << "\n";
}
F. Make Them Similar
题意:给你\(n\)个数,找一个\(x\),使得每个数在二进制表示下\(1\)的个数相同。
观察到数据范围为\(2^{30}-1\),有一个办法是折半搜索。
我们按位考虑,范围前\(15\)位和后\(15\)位,那么可以先暴力枚举前\(15\)位,把数据存下来,然后暴力枚举后\(15\)位,看前面有没有对应的。对于每个只包含前\(15\)位的\(x\),记\(c_i\)为\(a_i\)异或\(x\)后的\(1\)的个数,\(d_i\)为和只包含后\(15\)位异或后\(1\)的个数。那么需要\(c_i + d_i = c_1 + d_1\),因为前后是分开枚举的,那么我们把\(c, d\)分开:\(c_i - c_1 = d_1 - d_i\)。那么就可以暴力所有\(c_i - c_1\),后面看\(d_1 - d_i\)有没有对应的数组就行。可以用\(map\)存数组和\(x\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::map<std::vector<int>, int> mp;
for (int x = 0; x < 1 << 15; ++ x) {
std::vector<int> c(n);
int c0 = __builtin_popcount((a[0] >> 15) ^ x);
for (int i = 1; i < n; ++ i) {
c[i] = __builtin_popcount((a[i] >> 15) ^ x) - c0;
}
mp[c] = x;
}
for (int x = 0; x < 1 << 15; ++ x) {
std::vector<int> d(n);
int d0 = __builtin_popcount((a[0] & ((1 << 15) - 1)) ^ x);
for (int i = 1; i < n; ++ i) {
d[i] = d0 - __builtin_popcount((a[i] & ((1 << 15) - 1)) ^ x);
}
if (mp.count(d)) {
std::cout << (mp[d] << 15) + x << "\n";
return;
}
}
std::cout << -1 << "\n";
}