[CF1954] Educational Codeforces Round 164 (Rated for Div. 2) 题解
[CF1954] Educational Codeforces Round 164 (Rated for Div. 2) 题解
A. Painting the Ribbon
最优策略是染 \(\lceil \dfrac{n}{m} \rceil\) 个颜色,然后 Bob 会把剩下的都染成这个颜色
void work() {
int n, m, k, c;
cin >> n >> m >> k;
c = (n + m - 1) / m;
cout << (k >= n - c ? "NO" : "YES") << '\n';
return ;
}
B. Make It Ugly
波动不超过一,答案是波动间隔的 \(min\)。
int n, a[N];
vector<int> c;
void work() {
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
int x = a[1];
a[0] = -1, a[n + 1] = -1;
c.clear();
c.push_back(0);
for(int i = 1; i <= n; i ++) if(a[i] ^ x) c.push_back(i);
c.push_back(n + 1);
int mx = 1e9;
for(int i = 1; i < c.size(); i ++) {
mx = min(mx, c[i] - c[i - 1] - 1);
}
cout << (mx >= n ? -1 : mx) << '\n';
return ;
}
C. Long Multiplication
和一定,差小积大。
D. Colored Balls
有趣的计数。
一个颜色集合的 value 是
\[\max(\max a_i, \lceil \dfrac{\sum a_i} 2 \rceil)
\]
根据这个观察,我们可以计数 value 是 \(\max a_i\) 和 \(\sum\) 的。
如果把 \(a\) 排序,那么可以设计一个懂得都懂的 01 背包,然后转移前计算 \(a_i\) 的贡献即可。
sort(a + 1, a + n + 1);
f[0] = 1;
for(int i = 1; i <= n; i ++) {
for(int j = 0; j <= a[i]; j ++)
ans = (ans + f[j] * a[i] % mod) % mod;
for(int j = a[i] + 1; j <= 5000; j ++)
ans = (ans + ((j + a[i] + 1) / 2) * f[j] % mod) % mod;
for(int j = 5000; j >= a[i]; j --)
f[j] = (f[j] + f[j - a[i]]) % mod;
}
cout << ans << '\n';
E. Chain Reaction
\(k = 1\) 是积木大赛,\(k > 1\) 就把 \(a_i\) 除以 \(k\) 上取整之后做积木大赛。
这是一个上取整整除分块的形式,\(a\) 的变化次数是 \(O(n\sqrt V)\) 级别的,差分数组的变化也是这个级别,用整除分块找到变化的时刻然后维护答案即可。
上取整整除分块与下取整唯一的不同就是如果 \(x\bmod r =0\),那么 \(r\) 会成为下一段的左端点,其它情况都会向上加一。
稍微卡常,时间复杂度:\(O(n\sqrt V)\)。
// Problem: E. Chain Reaction
// Contest: Codeforces - Educational Codeforces Round 164 (Rated for Div. 2)
// Author: Moyou
// Copyright (c) 2024 Moyou All rights reserved.
// Date: 2024-04-12 23:31:10
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
// #define int long long
using namespace std;
const int N = 1e5 + 10;
int n, a[N], b[N], d[N], mx;
vector<int> c[N];
long long ans;
void chk(int k, int i) {
b[i] = (a[i] + k - 1) / k;
if(d[i] >= 0) ans -= d[i];
d[i] = b[i] - b[i - 1];
if(d[i] >= 0) ans += d[i];
if(d[i + 1] >= 0) ans -= d[i + 1];
d[i + 1] = b[i + 1] - b[i];
if(d[i + 1] >= 0) ans += d[i + 1];
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i], b[i] = a[i], mx = max(mx, a[i]), d[i] = a[i] - a[i - 1];
for(int i = 1; i <= n; i ++) if(d[i] >= 0) ans += d[i];
for(int i = 1; i <= n; i ++) {
bool flg = 0;
for(int l = 1, r; l <= a[i]; l = r + 1) {
r = a[i] / (a[i] / l);
if(!flg) c[l].push_back(i);
else flg = 0;
if(a[i] % r == 0) {
if(l != r) {
c[r].push_back(i);
flg = 1;
}
}
}
}
cout << ans << ' ';
for(int k = 2; k <= mx; k ++) {
for(auto i : c[k]) chk(k, i);
cout << ans << ' ';
}
return 0;
}
总结
打得还行,唯一的遗憾是 E 题没写完,主要是没写过上取整整除分块调了半天,后来用 STL 帮忙写还被卡常了,只能老老实实写上取整整除分块了·。