[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 幫忙寫還被卡常了,只能老老實實寫上取整整除分塊了·。