Educational Codeforces Round 176 (Rated for Div. 2)
A. To Zero
题意:给你一个\(n\)和\(k\), \(k\)是一个奇数,你每次可以选择\(1\)到\(k\)的某个数让\(n\)减掉。但如果\(n\)是奇数你也必须选奇数,是偶数你也必须选偶数。
如果\(n\)是奇数,我们就减去\(k\), 因为奇数减奇数后就是偶数,不能再减回奇数,所以应该在能减奇数的时候减。然后就是一直减\(k-1\),直到\(n \leq k - 1\),就减去\(n\)就行了。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
int ans = 0;
if (n & 1) {
n -= k;
++ ans;
}
ans += (n + k - 2) / (k - 1);
std::cout << ans << "\n";
}
B. Array Recoloring
题意:给你\(n\)个数,一开始你要选\(k\)个数填色,然后得到它们的总和的价值。最后你要一步一步扩展这\(k\)个数,每次可以将一个相邻的位置填色,你可以得到最后填色的位置的价值。求最大价值。
我们可以得到\(k+1\)个数,但直接取最大的\(k+1\)个是不行的,因为可能选了\(k\)个最大的后第\(k+1\)大的不能被最后选到。
我们可以枚举选的数的左右边界,也就是对于\([i, j]\)我们必选\(a_i, a_j\),那么只需要算出区间里的前\(k-1\)大的数就行了。可以用优先队列维护。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
i64 ans = 0;
if (k == 1) {
for (int i = 1; i < n; ++ i) {
ans = std::max(ans, a[i] + a[0]);
}
for (int i = 0; i + 1 < n; ++ i) {
ans = std::max(ans, a[i] + a[n - 1]);
}
std::cout << ans << "\n";
return;
}
for (int i = 0; i < n; ++ i) {
std::priority_queue<i64, std::vector<i64>, std::greater<i64>> heap;
i64 sum = 0;
for (int j = i + 1; j < n; ++ j) {
if (j - i - 1 >= k - 1) {
ans = std::max(ans, sum + a[i] + a[j]);
}
if (heap.size() == k - 1) {
if (k != 1 && a[j] > heap.top()) {
sum -= heap.top();
heap.pop();
sum += a[j];
heap.push(a[j]);
}
} else {
sum += a[j];
heap.push(a[j]);
}
}
}
std::cout << ans << "\n";
}
C. Two Colors
题意:有长度为\(n\)个木板和\(m\)中颜色,每种颜色只能涂成一个区间,并且每个颜色的数量是\(a_i\)。你要把木板填成两种颜色。问有多少种填法。
对于一个\(a_i\),和它一起填木板的\(a_j\)至少是\(n-a_i\)。然后模拟一下,如果规定\(i\)涂左边,\(j\)涂右边,那么长度为\(a_j\)的板子和\(a_i\)有\(a_j - (n - a_i - 1)\)种填法。那么我们可以记录\(a_i\)为\(k\)的颜色的个数,已经\(a_i\)为\(k\)的\(a_i\)的和,用树状数组维护维护前缀和。我们从前往后枚举,那么对于一个数量为\(a_i\)的颜色,贡献为\(sum(n - a_i, n) - cnt(n - a_i, n) \times (n - a_i - 1)\)。注意要特别\(a_i\)等于\(n\)的颜色的贡献。
点击查看代码
template <class T>
struct Fenwick {
int n;
std::vector<T> tr;
Fenwick(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
tr.assign(_n + 1, T{});
}
void add(int x, const T &v) {
for (int i = x; i <= n; i += i & -i) {
tr[i] = tr[i] + v;
}
}
T query(int x) {
T res{};
for (int i = x; i; i -= i & -i) {
res = res + tr[i];
}
return res;
}
T sum(int l, int r) {
return query(r) - query(l - 1);
}
};
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(m);
for (int i = 0; i < m; ++ i) {
std::cin >> a[i];
}
Fenwick<i64> sum(n + 1), cnt(n + 1);
i64 ans = 0;
for (int i = 0; i < m; ++ i) {
if (a[i] != n) {
ans += sum.sum(n - a[i], n - 1) - cnt.sum(n - a[i], n - 1) * (i64)(n - a[i] - 1);
ans += cnt.sum(n, n) * a[i];
} else {
ans += sum.sum(1, n - 1) + cnt.sum(n, n) * (n - 1);
}
sum.add(a[i], a[i]);
cnt.add(a[i], 1);
}
std::cout << ans * 2 << "\n";
}
D. Equalization
题意:给你一个\(x, y\),你每次可以选一个\(k\),使得\(x\)或者\(y\)除以\(2^k\)向下取整。每个\(k\)只能选一次,且代价为\(2^k\),求\(x,y\)相等的最小代价。
在二进制下观察,除\(2^k\)就是右移\(k\)位。那么我们从高到低看,假设\(x\)有\(n\)位,\(y\)有\(m\)位,那么如果对于\(i \in [1, \min(n, m)]\)的\(x_n\)到\(x_{n-i+1}\)等于\(y_m\)到\(y_{m-i+1}\), 则我们可以使\(x\)右移\(n-i\)位和\(y\)右移\(m-i\)为使得它们相等。
那么我们记\(f[i][j]\)为\(x\)右移\(i\)位\(y\)右移\(j\)位的最小代价。那么这是一个背包问题:有两个容量,一些物品,要求每个物品只能用一次,使得正好装满两个容量的最小代价。那么我们可以\(01\)背包预处理。
然后求出\(x,y\)的二进制表示,按上述从高到低看,取最小值。然后假设结果是\(x, y\)都变成\(0\),那么不必恰好右移多少位,可以枚举右移数大于\(n, m\)的答案。最后如果某个数二进制表示是另一个数的前缀,也要特殊处理一下。
点击查看代码
const int N = 60;
i64 f[N][N];
void init() {
for (int i = 0; i < N; ++ i) {
for (int j = 0; j < N; ++ j) {
f[i][j] = 1e18;
}
}
f[0][0] = 0;
for (int k = 1; k < N; ++ k) {
for (int i = N - 1; i >= 0; -- i) {
for (int j = N - 1; j >= 0; -- j) {
if (i >= k) {
f[i][j] = std::min(f[i][j], f[i - k][j] + (1ll << k));
}
if (j >= k) {
f[i][j] = std::min(f[i][j], f[i][j - k] + (1ll << k));
}
}
}
}
}
void solve() {
i64 x, y;
std::cin >> x >> y;
if (x == y) {
std::cout << 0 << "\n";
return;
}
std::vector<int> a, b;
while (x) {
a.push_back(x % 2);
x /= 2;
}
while (y) {
b.push_back(y % 2);
y /= 2;
}
int n = a.size(), m = b.size();
i64 ans = 1e18;
for (int i = N - 1; i >= std::max(n, m); -- i) {
ans = std::min({ans, f[i][i], f[n][i], f[i][m]});
}
bool flag = true;
for (int i = n, j = m; i >= 1 && j >= 1; -- i, -- j) {
ans = std::min(ans, f[i][j]);
if (a[i - 1] != b[j - 1]) {
flag = false;
break;
}
}
if (flag) {
ans = std::min(ans, f[0][std::abs(n - m)]);
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int T = 1;
init();
std::cin >> T;
//scanf("%d", &T);
while (T -- ) {
solve();
}
return 0;
}

浙公网安备 33010602011771号