先是校验值,显然对于集合中要M对(数差的平方) 的和MAX,可知每次选先排序,然后选最大和最小,次大和次小…即可
而又给了个T,问咋样能使每组的校验值最大不超过T,最少能分几组,可得目标为每组的校验值尽量大
因为数差的平方的和为单调递增,所以我们可以采用二分枚举r的位置,但因为二分具有不稳定性,极限情况会超时,所以采用倍增
同理,在进行排序的时候,因为快排不稳定,所以利用倍增时我们已经把(l,r) 排完序,这时只需要对(r+1,r+len)快排,再进行归并排序,可以降低复杂度
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MA = 5e5+10;
int n,m;
int a[MA];//原数组
int b[MA],p[MA],ans;//辅助数组
ll T;
void merge(int l,int mid, int r){ //归并排序 ,并将排好的暂存于p
int i = l,j = mid;
for (int z = l; z <= r; z++){
if(i >= mid || (b[i] > b[j] && j <= r)) p[z] = b[j++];
else p[z] = b[i++];
}
}
bool check(int l,int mid, int r){
//对l到r进行排序
for (int i = mid; i <= r; i++) b[i] = a[i];
sort (b+mid,b+r+1);
merge(l,mid,r);
//计算校验值
ll sum = 0;
for (int i = 0; i < m && i < r-l+1 >> 1; i++){
sum +=(long long) (p[l+i] - p[r-i]) * (p[l+i] - p[r-i]);
}
if( sum <= T) { //如果成功则存下排好序的数组
for (int i = l; i <= r; i++) b[i] = p[i];
return true;
}
else return false;
}
void work(){
ans = 0;
int l = 0, r = 0,len = 1;
b[l] = a[l];//为后面的归并排序做准备
while(r < n){
if(!len){//找到一个,准备下一个
len = 1;
l = (++r);
ans++;
b[l] = a[l];
}
else if( r+len < n && check(l,r+1,r+len)){//成功倍增
r += len;
len <<= 1;
}
else len >>= 1; //倍增失败退位
}
return ;
}
int main()
{
int ci;
scanf("%d",&ci);
while(ci--)
{
scanf("%d%d%lld",&n,&m,&T);
for (int i = 0; i < n; i++){
scanf("%d",&a[i]);
}
work();
cout<<ans<<endl;
}
return 0;
}