CF1954E 思路分享(整除分块,铲雪)
https://codeforces.com/problemset/problem/1954/E
题意
给定长度为 \(n\) 的序列 \(a\),定义 \(f(k)\):
-
初始第 \(i\) 个数为 \(a_i\).
-
每次操作选择一个所有数均大于 \(0\) 的区间,将区间内所有数减 \(k\).
-
\(f(k)\) 为让所有数均小于等于 \(0\) 的最少操作次数.
对 \(k=1,2,\cdots,\max(a_1,a_2,\cdots,a_n)\) ,求 \(f(k)\).
\(1\le n,a_i \le 10^5\).
思路
定义 \(b_i\) 为让 \(a_i\) 小于等于 \(0\) 的操作次数,那么 \(b_i = \left\lceil\frac{a_i}{k}\right\rceil\).
于是问题变成让 \(b_i\) 全 \(0\),这是经典的铲雪问题,最少操作次数为
\[\sum_{i=1}^{n}{\max(0,b_i-b_{i-1})}
\]
注意 \(b_i\) 的形式,考虑整除分块.
让所有 \(a_i\) 参与分块退化太严重,考虑贡献法,每次只考虑相邻位置,差分维护贡献即可,注意向上取整分块的写法.
时间复杂度 \(\mathcal{O}(n\sqrt{V})\),\(V\) 是 \(a_i\) 的上界.
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve(){
int n;
cin >> n;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
}
int l = 1;
int V = *max_element(a.begin()+1,a.end());
vector<ll> res(V+2);
for (int i=1;i<=n;i++){
int l = 1;
while (l<=V){
int r = min(a[i-1]-1>=l?(a[i-1]-1)/((a[i-1]-1)/l):V,a[i]-1>=l?(a[i]-1)/((a[i]-1)/l):V);
res[l] += max(0,(a[i]+l-1)/l-(a[i-1]+l-1)/l);
res[r+1] -= max(0,(a[i]+l-1)/l-(a[i-1]+l-1)/l);
l = r+1;
}
}
for (int i=1;i<=V;i++){
res[i] += res[i-1];
cout << res[i] << ' ';
}
cout << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号