解题思路

考虑手玩样例找性质。先约定实力值为 l ∼ r l\sim r lr 的小组记作 ( l , r ) (l,r) (l,r)

对于这组样例:

7
1 2 2 3 3 4 5

我们以能力值为横轴画出柱状图。

如果划分成 ( 1 , 5 ) (1,5) (1,5) ( 2 , 3 ) (2,3) (2,3),人最少的组有 2 人。

而分成 ( 1 , 3 ) (1,3) (1,3) ( 2 , 5 ) (2,5) (2,5),的情况更优。因为这样人最少的组有 3 人。

于是我们可以知道,对于任意两组 ( i , j ) (i,j) (i,j) ( k , l ) (k,l) (k,l),若 i ≤ k i\le k ik l ≤ j l\le j lj,即 ( i , j ) (i,j) (i,j) 包含 ( k , l ) (k,l) (k,l),则重组为 ( i , l ) (i,l) (i,l) ( k , j ) (k,j) (k,j) 一定不劣。

因此最优解中的分组两两不包含, l l l 更小的组, r r r 也一定更小。

考虑用队列维护每组的人数,并依次处理上面柱状图中的每一列。队列中小组的 l l l r r r 均单调不降。

设当前列高度为 h h h,队列长度为 l e n len len,则具体操作如下:

  1. h ≥ l e n h\ge len hlen,则入队 h − l e n h-len hlen 0 0 0
  2. h < l e n h<len h<len,则弹出 l e n − h len-h lenh 个元素并更新答案。

最后队列中的元素整体 + 1 +1 +1,用懒标记 tag 维护即可。

细节:特判高度为 0 0 0 的列,最终必须清空队列并更新答案。

时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn),瓶颈为排序。

Code

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i(a);i<b;++i)
#define rept(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
constexpr int N=1e5+5,INF=2e9;
int a[N],q[N];
int n,ans=INF;
int l,r,tag;
int lst,cur,h;
void calc(){
    // lst:上一列位置
    // cur:当前列位置
    // h:当前列高度
    if(cur-lst>1){  // 中间有高度为0的列
        while(l<r) ans=min(ans,q[l++]+tag);
        rep(i,0,h) q[r++]=-tag;
        ++tag;
        return;
    }
    if(h>=r-l) rep(i,0,h-(r-l)) q[r++]=-tag;
    else rep(i,0,r-l-h) ans=min(ans,q[l++]+tag);
    ++tag;
}
signed main(){
    cin.tie(0)->sync_with_stdio(0);
    cin>>n;
    rept(i,1,n) cin>>a[i];
    sort(a+1,a+n+1);
    cur=a[1],h=1,lst=-INF;
    rept(i,2,n){
        if(a[i]==cur) ++h;
        else{
            calc();
            lst=cur;
            cur=a[i];
            h=1;
        }
    }
    calc();
    while(l<r) ans=min(ans,q[l++]+tag);  // 清空队列,更新答案
    cout<<ans;
    return 0;
}

Update 2025.2.11:修正时间复杂度分析。
Update 2025.6.15:微调图片与文字间距。
Update 2025.6.16:调整公式上下标。
Update 2025.12.6:优化语言表达,修正若干错误,重构代码,感谢 M_Cat 的指正。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐