下面要介紹給大家的題選自《演算法競賽高階指南》,這道題困擾了我乙個月,今天終於被我乾掉了,下面給出題目和**,希望對大家有幫助。
給定乙個整數 m,對於任意乙個整數集合 s,定義「校驗值」如下:
從集合 s 中取出 m 對數(即 2∗m 個數,不能重複使用集合中的數,如果 s 中的整數不夠 m 對,則取到不能取為止),使得「每對數的差的平方」之和最大,這個最大值就稱為集合 s 的「校驗值」。
現在給定乙個長度為 n 的數列 a 以及乙個整數 t。
我們要把 a 分成若干段,使得每一段的「校驗值」都不超過 t。
求最少需要分成幾段。
輸入格式
第一行輸入整數 k,代表有 k 組測試資料。
對於每組測試資料,第一行包含三個整數 n,m,t 。
第二行包含 n 個整數,表示數列a1,a2…an。
輸出格式
對於每組測試資料,輸出其答案,每個答案佔一行。
資料範圍
1≤k≤12,
1≤n,m≤500000,
0≤t≤1018,
0≤ai≤220
大家可以看到,這個資料量。。。出題老師真的是毒瘤之王 nb,可以想出這麼nb的題目,這種題目應該點讚給大家都做一做。
#include
#include
#include
#define ll long long
using
namespace std;
const
int n=
500010
;ll a[n]
,t1[n]
,t2[n]
;int n,m;
ll k;
bool
check
(int l,
int mid,
int r)
}int left=l,right=r,cnt=0;
ll res=0;
while
(cntleft)
if(res<=k)
return0;
}int
main()
l=r=r+1;
}printf
("%d\n"
,res);}
return0;
}
首先是倍增模板(可替代原先的二分**,但是平均效能不及二分)
int
find
(int x)
return r;
}
然後是合併兩個有序集合
void
merge
(int l,
int mid,
int r)
//[l,mid-1]和[mid,r]
}
然後把兩個核心**帶入源**看,應該就會輕鬆一點了,用倍增法不斷分割區間,最終得到答案,**非常的漂亮(不愧是李煜東大佬) 天才ACM(倍增)
題目參考題解 很好。首先考慮我們用貪心證明兩個東西 如果第 i 個可以歸到前面的部分就歸到前面的部分,不要放到後面的部分,反正放到後面也只會讓校驗值增大,還不如不放。對於乙個數列而言,如何求校驗值?答案是最大的減最小的的平方加上次大的.至於證明,首先,如果對於 x y 2 x y 而數列中有 沒有用...
AcWing 109 天才ACM 倍增
題目鏈結 這道題最基本的想法就是一次尋找每個區間,對於每個區間,用二分來判定其最大長度。每次check的時候,對區間排序,不斷取出不大於m對最大值與最小值求值即可。然後你就喜提tle了,笑如果用倍增來代替二分的話能過,不過其實倍增最壞複雜度和二分一樣,應該是資料沒有刻意來卡倍增。下面先給出倍增的 c...
AcWing 109 天才ACM 倍增 歸併
給定乙個整數 mm,對於任意乙個整數集合 ss,定義 校驗值 如下 從集合 ss 中取出 mm 對數 即 2 m2 m 個數,不能重複使用集合中的數,如果 ss 中的整數不夠 mm 對,則取到不能取為止 使得 每對數的差的平方 之和最大,這個最大值就稱為集合 ss 的 校驗值 現在給定乙個長度為 n...