天才ACM(倍增)

2022-05-09 15:54:55 字數 2977 閱讀 3576

題目參考題解:

很好。首先考慮我們用貪心證明兩個東西:

如果第\(i\)個可以歸到前面的部分就歸到前面的部分,不要放到後面的部分,反正放到後面也只會讓校驗值增大,還不如不放。

對於乙個數列而言,如何求校驗值?答案是最大的減最小的的平方加上次大的...。

至於證明,首先,如果對於\((x-y)^2(x>y)\),而數列中有 沒有用的比\(x\)更大或比\(y\)更小的數字的話,那麼我們肯定要用啊。

第二,對於\(a而言,最大的方案肯定是:\((a-d)^2+(b-c)^2\),要證明的話全部化成平方和就變成了問兩兩數字相乘最小的問題了:\(a^2+b^2+c^2+d^2-2ad-bc\),這個大家肯定都會證明的吧,不會證明看這裡:

通過這兩個結論就可以證明了。

因為長度越長校驗值越大,根據這個可以二分,也就是二分+check,但是如果每個塊都很小的話時間複雜度就很容易被卡成:\(o(kn^2log^2n)\),。

這個時候就要用到倍增了,倍增演算法完美解決了二分處理小資料時無力的問題,倍增的流程是:

確定乙個\(x=1,r=1\),然後如果\([1,r+x]\)可以的話,那麼\(r+=x,x<<=1\),並重複1操作,但是如果不可以的話,\(x>>=1\)並跳到2操作。

如果[1,r+x]可以的話,\(r+=x\),不管可不可以,\(x>>=1\)並重複2操作直到\(x=0\)。

當然還有一種較慢的倍增:是如果不可以\(x>>=1\),可以\(x<<=1\),但是會慢(因為其實也是跟上面一樣的流程,但是在2步驟中一旦可以會\(x<<=1\)又要花一次\(x>>=1\)來重回正軌)。

(其實我一開始的寫法是是1操作沒有\(r+=x\),跳到2操作時再\(r=x\),但是沒必要,還會慢)

當然,上面的三種做法,單次的時間複雜度都是\(o(\log)\)(\(t\)為最終的\(r\)),因此倍增是乙個時間複雜度很優秀的演算法,而且在處理資料塊很小時更是展現出比二分大得多的優越性。(至於為什麼,你可以把第乙個步驟看成找最大\(?\)使得\(2^-1≤t\),至於第二個步驟,則是又把\(?\)遍歷一遍,所以是\(o(\log)\))

採用快速排序的時間複雜度是\(o(nlog^2n)\)。

證明我先寫的下面的證明,哈哈:

設第\(i\)個塊長度為\(l_i\),且在倍增第乙個步驟中\(x\)最大去到了\(xx\)。

注:計算校驗值的時間複雜度是塊長,而排序的複雜度大於等於塊長,因此預設忽略校驗值的時間複雜度計算。

第乙個步驟是乙個\(log\),我們不管他,我們要證的是第二個步驟,第乙個步驟完畢後,確定的\(r\)大於等於\(\frac\),因為要check \(logl_i\)次,所以時間複雜度為:\(o(rlogl_ilogr)=r(l_ilog^2l_i)\)。

所有塊合併起來的時間複雜度就是:\(o(nlog^2n)\)。

常數小可以ac此題。

#include#include#include#define  n  510000

using namespace std;

int a[n],b[n],n,m;

long long t;

inline int mymin(int x,int y)

int main()

cnt*=2;

}if(cnt!=n-now)

}cnt>>=1;}}

now+=r;

ans++;

} printf("%d\n",ans);

} return 0;

}

我們上面採用的是快速排序,但是如果用類似歸併排序的方法呢?

即我現在已經知道了\(x\),然後要對\(x+y\)進行排序,那麼就對\(y\)進行排序,然後合併。

這個優化在較快的倍增中的第乙個步驟中是毫無優化的,因為:\(\frac*\log

}*2+n=n\log\)。

但是再第二個步驟中,就不會因為長度較小的\(y\)而重複排序了,優化程度很大。

(下面證明用的是目前講的最快的倍增打法)

那麼我們現在就來證明一波時間複雜度:

設第\(i\)個塊長度為\(l_i\),且在倍增第乙個步驟中\(x\)最大去到了\(xx\)。

注:計算校驗值的時間複雜度是塊長,而排序的複雜度大於等於塊長,因此預設忽略校驗值的時間複雜度計算。

對於第乙個步驟而言:\(o(1*log(1)+3*log(2)+7*log(4)+...+(2*xx-1)*log(xx)+(4*xx-1)*log(xx*2))=o(xx\log)\)(根據\(a_1\log+a_2\log+...+a_i\log≤sum\log\)得出,\(sum\)為總和)

對於第二個步驟而言:

合併的話,即使假設單次合併的的時間是最終塊長的兩倍,那麼時間複雜度也是:\(o(log_l_i)\),所以就是分析快速排序的時間複雜度:\(o(1*log(1)+2*log(2)+4*log(4)+...+\frac*log(\frac)=o(xx\log)\),因此對於乙個塊而言,時間複雜度即為:\(o(l_i\log)\),所有塊時間複雜度一合併,就是\(o(nlogn)\)了。

**:

#include #include using namespace std;

typedef long long ll;

const int n = 500005;

int n, m;

int ans;

ll t;

ll w[n], t[n];

ll tmp[n];

ll sq(ll x)

bool check(int l, int mid, int r) // 判斷區間 [l, r) 是否合法,並將 t 中的 [l, mid) 區間和 [mid, r) 區間合併到 tmp 中

int main()

else len >>= 1;

}start = end;

ans ++ ;

}printf("%d\n", ans);

}return 0;

}

倍增問題之 天才ACM

下面要介紹給大家的題選自 演算法競賽高階指南 這道題困擾了我乙個月,今天終於被我乾掉了,下面給出題目和 希望對大家有幫助。給定乙個整數 m,對於任意乙個整數集合 s,定義 校驗值 如下 從集合 s 中取出 m 對數 即 2 m 個數,不能重複使用集合中的數,如果 s 中的整數不夠 m 對,則取到不能...

AcWing 109 天才ACM 倍增

題目鏈結 這道題最基本的想法就是一次尋找每個區間,對於每個區間,用二分來判定其最大長度。每次check的時候,對區間排序,不斷取出不大於m對最大值與最小值求值即可。然後你就喜提tle了,笑如果用倍增來代替二分的話能過,不過其實倍增最壞複雜度和二分一樣,應該是資料沒有刻意來卡倍增。下面先給出倍增的 c...

AcWing 109 天才ACM 倍增 歸併

給定乙個整數 mm,對於任意乙個整數集合 ss,定義 校驗值 如下 從集合 ss 中取出 mm 對數 即 2 m2 m 個數,不能重複使用集合中的數,如果 ss 中的整數不夠 mm 對,則取到不能取為止 使得 每對數的差的平方 之和最大,這個最大值就稱為集合 ss 的 校驗值 現在給定乙個長度為 n...