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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章