进阶指南--天才ACM

先是校验值,显然对于集合中要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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值