ACwing 天才ACM 题解

题面入口:https://www.acwing.com/problem/content/111/
题目大意,将一个数列划分成最少的几段,满足每段内的数据集合中,取M对最大最小数出来,将其取出来的每对数求差值并平方,并求这M对的差值平方的求和值S,这个值不能超过指定的T。
题目分析:
  从划分后的集合中选出M对数,让每对数的差的平方的和最大值为一个贪心模型,我们只需要将集合中的元素按从小到大排序,然后把最大数和最小数配对,再将次大数和次小数配对,依次类推即可。
  为了划分尽可能少的区间,那么只要每个区间都尽可能大,那么就是最优方案,于是原问题也就转化为确定了一个左端点,右端点在哪个位置,使权值最大化,不超过T。
  确定右端点一个个往后加并试探是否不超过T,时间复杂度会比较大。那么我们可以增加的方式变得有规律,步子要大,可以用倍增的思想。假设当前处理的区间是[left,right],尝试待确定的右端点newright,以及一次要往后增加的长度len。
  因此我们接下来操作可以这样,先令newright=right + 2 * len;
,然后计算区间[left , newright]的权值与T的关系,如果比T大,则令len/2,否则right = newright,len*2;。
  在计算一个范围内的取m对的差值平方和,便捷的方式是将其有序,两端取后计算。那么需要要对区间进行排序,在新增加区间的时候,不用全部重新排一遍,根据维护的思想,我们已经有了一段序列是有序的,将新增加的区间进行排序,然后二路归并到已确定的序列中让其有序,可以提高速度。
  具体代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 500000+6;
int a[MAXN],b[MAXN],t[MAXN];
LL T;
int n,m,k;
//ºÏ²¢Á½¶ÎÓÐÐòµÄÇø¼äÖµ 
void merge(int left,int right,int newright){
	int i = left ,j = right+1;
	for(int k = left ; k<= newright; k++){
		if(j> newright ||(i<= right && b[i] <= b[j]))
			t[k] = b[i++];
		else
			t[k] = b[j++];
	}
}
//¼ÆËãÓÐÐò·¶Î§left - rightÖ®¼äµÄM¶Ô²îֵƽ·½µÄºÍ¡£ 
LL calc(int left,int right){
	LL sum = 0;
	for(int i = left ,j = right,k = m; i<j && k >0 ; i++,j--,k--){
		sum = sum +1LL * (t[i]-t[j])*(t[i]-t[j]);
	}
	return sum;
} 
int main(){
	scanf("%d",&k);
	while(k--){
		int  cnt = 0; 
		scanf("%d%d%lld",&n,&m,&T);
		for(int i = 1;i<= n; i++){
			scanf("%d",&a[i]);
		}
		b[1] = a[1];
		int left = 1,right = 1,newright,len = 1;
		while(right < n){
			newright = right + len;
			//Èç¹û³ö½çÔò»Øµ½n. 
			if(newright > n ) newright = n;
			//¸´ÖÆright+1 -- newright Ö®¼äµÄÊýÖÁbÊý×éÖб¸Óᣠ
			for(int i = right + 1; i <= newright; i ++) b[i] = a[i];
			sort(b+ right+1, b+ newright + 1);
			merge(left,right,newright);
			LL sumpow = calc(left,newright);
			if(sumpow > T) len = len /2;
			else{
				right = newright;
				len = len * 2;
				for(int i = left; i<= right ; i++) b[i] = t[i];
			} 
			if(len == 0) {
				//Ò»¸ö·Ö¶Î½áÊø£¬ÐµķֶοªÊ¼¡£ 
				left = right + 1;
				len = 1; 
				cnt ++; 
			}
		}
		printf("%d\n",cnt + 1);
	}
    return 0;
}
发布了88 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/xuechen_gemgirl/article/details/96873069