AcWing 3993. 取石子


取石子

题意

\(n\) 堆石子,每堆有 \(a_1, a_2, a_3...a_n\) 个石子,进行若干轮,使得每堆石子的数量相同。

每一轮:选择一个限定值 \(h\) ,将所有石子数量大于 \(h\) 的堆去除直到剩下 \(h\) 个石子。注意每一轮去除的石子数量不能超过 \(k\)

给定 \(n、k\) 与每堆石子的数量,求最少进行几轮可以达成目标。

\(1 \le n \le 2 \times 10^5, n \le k \le 10^9, 1 \le a_i \le 2 \times 10^5\)

分析

从高度最大的石子堆开始取,如果能取的数量不超过 \(k\) ,那么累加到下一次,否则轮数加一。

Code

#include 
#include 
#include 

using namespace std;

const int N = 200010;

int n, k;
int cnt[N];

int main ()
{
    cin >> n >> k;
    int v = 2e6, m = 0;
    for (int i = 1, x; i <= n; i ++ ) cin >> x, cnt[x] ++ , v = min(v, x), m = max(m, x);

    // 如果初始相同高度,那么不需要轮次
    if (v == m) { puts("0"); return 0; }
    
    long long cnts = 0;
    int ans = 0;
    // 从最大堆开始,直到最小堆,因为最后的状态一定使全v
    for (int i = m; i > v; i -- )
    {
        // 贪心:当石子个数累计超过 k 时才进行一轮操作,取出高度大于 i 的剩余石子
        if (cnts + cnt[i] > k)
        {
            cnts = cnt[i];
            ans ++ ;
        }
        else cnts += cnt[i];
        // 当前的剩余石子累加到下一层
        cnt[i-1] += cnt[i];
    }
    // 最后剩余的cnts再每个取一个,进行一次操作
    cout << ans + 1 << endl;
    return 0;
}