嘘~ 正在从服务器偷取页面 . . .

模拟赛题讲解[34]


模拟赛题讲解[34]

来自 yukuai26 2023-08-06 noi.ac #3281

题目描述

给你一个长度为 \(n\) 的序列,以及一个数字 \(k\)

每次你可以选择一对 \((i,j)\),使得 \(a_i\) 加一,\(a_j\) 减一。

问最少经过多少次操作,使得数列中任意两个数的差不超过 \(k\)。数据保证有解。

输入格式

第一行两个整数 \(n,k\) 第二行 \(n\) 个整数,表示 \(a_i\)

输出格式

输出最小的答案

输入样例

4 3
1 5 1 10

输出样例

4

数据范围

保证 \(1 \leq n \leq 100000,1 \leq K \leq 100000,1 \leq a_i \leq 100000\)且在该范围内等概率随机生成

题解

显然我们每次修改的两个数分别为最大值和最小值。

并且每次修改一定会把最小值和最大值中出现次数最少的那个给灭掉。

那么就模拟一下这个过程就好了。

时间复杂度 \(\mathcal{O}(n+\max\{a_i\})\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))

int n,k,l = INF,r,ans,a[N],b[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> k;
    for(int i = 1; i <= n; i++) {
        cin >> a[i], ++b[a[i]]; up(r, a[i]); down(l, a[i]);
    }
    while(r - l > k)
    {
        int x = min(b[l], b[r]); ans += x;
        b[r - 1] += x; b[r] -= x; if(!b[r]) --r;
        b[l + 1] += x; b[l] -= x; if(!b[l]) ++l;
    }
    cout << ans << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录