題面入口:
題目大意,將乙個數列劃分成最少的幾段,滿足每段內的資料集合中,取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對的差值平方和,便捷的方式是將其有序,兩端取後計算。那麼需要要對區間進行排序,在新增加區間的時候,不用全部重新排一遍,根據維護的思想,我們已經有了一段序列是有序的,將新增加的區間進行排序,然後二路歸併到已確定的序列中讓其有序,可以提高速度。
具體**如下:
#includeusing 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)
}//¼æëãóððò·¶î§left - rightö®¼äµäm¶ô²îöµæ½·½µäºí¡£
ll calc(int left,int right)
return sum;
} int main()
b[1] = a[1];
int left = 1,right = 1,newright,len = 1;
while(right < n)
if(len == 0)
} printf("%d\n",cnt + 1);
} return 0;
}
AcWing 109 天才ACM 倍增
題目鏈結 這道題最基本的想法就是一次尋找每個區間,對於每個區間,用二分來判定其最大長度。每次check的時候,對區間排序,不斷取出不大於m對最大值與最小值求值即可。然後你就喜提tle了,笑如果用倍增來代替二分的話能過,不過其實倍增最壞複雜度和二分一樣,應該是資料沒有刻意來卡倍增。下面先給出倍增的 c...
AcWing 109 天才ACM 倍增 歸併
給定乙個整數 mm,對於任意乙個整數集合 ss,定義 校驗值 如下 從集合 ss 中取出 mm 對數 即 2 m2 m 個數,不能重複使用集合中的數,如果 ss 中的整數不夠 mm 對,則取到不能取為止 使得 每對數的差的平方 之和最大,這個最大值就稱為集合 ss 的 校驗值 現在給定乙個長度為 n...
天才ACM(倍增)
題目參考題解 很好。首先考慮我們用貪心證明兩個東西 如果第 i 個可以歸到前面的部分就歸到前面的部分,不要放到後面的部分,反正放到後面也只會讓校驗值增大,還不如不放。對於乙個數列而言,如何求校驗值?答案是最大的減最小的的平方加上次大的.至於證明,首先,如果對於 x y 2 x y 而數列中有 沒有用...