题目链接 1384 -- Genius ACM

给定一个整数 m,对于任意一个整数集合 S,定义“校验值”如下:
从集合 S 中取出 m 对数(即 2*M 个数,不能重复使用集合中的数,如果 S 中的整 数不够 m 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值 就称为集合 S 的“校验值”。
现在给定一个长度为 n 的数列 A 以及一个整数 k。我们要把 A 分成若干段,使得 每一段的“校验值”都不超过 k。求最少需要分成几段。

#include<bits/stdc++.h>
using namespace std;
#define maxn 600005
#define LL long long
int a[maxn],b[maxn],c[maxn];
int n,m;
LL k;
void mem(int l,int mid,int r){
   int i=l,j=mid+1,zz=r,ii=l;
   while(i<=mid&&j<=r){
      if(b[i]<b[j]) c[ii++]=b[i++];
      else c[ii++]=b[j++];
   }
   while(i<=mid) c[ii++]=b[i++];
   while(j<=r) c[ii++]=b[j++];
}
bool xxx(int l,int mid,int r){
   mem(l,mid,r);
   LL ans=0,i=0,j=l,kk=r;
   while(j<kk&&i<m){
      LL z=0;
      z=1LL*(c[j++]-c[kk--]);
      ans+=z*z;
      i++;
   }
   if(ans>k) return 0;
   else return 1;
}
bool work(int l,int r,int rr){
   for(int j=rr+1;j<=r;j++) b[j]=a[j]; // 每次只用在b后面加上我们后来倍增的一段区间
   sort(b+rr+1,b+r+1);  // 把增加的一段排序 后面可以用归并
   if(xxx(l,rr,r)) return 1;
   return 0;
}
int main(){
   int t;
   cin>>t;
   while(t--){
      scanf("%d%d%lld",&n,&m,&k);
      for(int j=1;j<=n;j++){
         scanf("%d",&a[j]);
      }
      int ans=0;
      int l=1,p=1,r=l;
      b[1]=a[1];
      while(l<=n){
         if((r+p)<=n&&work(l,r+p,r)){
             for(int i=l;i<=r+p;i++) b[i]=c[i];  //把每次符合的序列按顺序放在b里面
             r+=p;
             p*=2;
         }else p/=2;
         if(p==0){
            l=r+1;
            r=l;
            p=1;
            ans++;
         }
      }
      cout<<ans<<endl;
   }
}