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;
}
posted @ 2026-07-02 15:46  kzssCCC  阅读(10)  评论(0)    收藏  举报