倍增字面上意思是:成倍地增加。當模擬乙個過程時,一步一步進行太慢,考慮把模擬的步數二進位制分解;經過一些預處理,每次可以模擬 \(2^i\) 步,從而達到優化複雜度的目的。
倍增主要模型有rmq,lca等。
例題給出乙個長度為 n 的環和乙個常數 k,每次可以從第 i 個點跳到第 (i + k) mod (n+1) 個點,總共跳 m 次。第 i 個點的權值為 a[i],求 m 次跳躍的起點的權值之和 mod 1e9 + 7 。
資料範圍:$ 1 ≤ n ≤ 10^6 , 1 ≤ m ≤ 10^ , 1 ≤ k ≤ n , 0 ≤ a[i] ≤ 10^9 $
問題分析
這裡顯然不能暴力模擬跳 m 次。因為 最大可到 \(10^\) 級別,如果暴力模擬的話,時間承受不住。
所以就需要進行一些預處理,提前整合一些資訊,以便於在查詢的時候更快得出結果。如果記錄下來每乙個可能的跳躍次數的結果的話,不論是時間還是空間都難以承受。
倍增思想:每個數都可以表示成二進位制的形式, 對於從每個點開始的 \(2^i\) 步,記錄乙個 go[i][x] 表示第 x 個點跳 \(2^i\) 步之後的終點,而 sum[i][x] 表示第 x 個點跳 \(2^i\) 步之後能獲得的點權和。對於跳 \(2^i\) 步的資訊,預處理的時候可以看作是先跳了 \(2^\) 步,再跳了 \(2^\) 步。
即有 \(sum[i][x] = sum[i-1][x]+sum[i-1][go[i-1][x]] ,且 go[i][x] = go[i-1][go[i-1][x]]\) 。
例如,從1到14的整個跳躍過程由 \(2^3 + 2^2 + 2^0\) 三步組成。也就是說,對於環上這 n 個位置,預處理出每乙個位置向前跳 1, 2, 4, ... 次的位置,則必然能夠到達 m。
實現**
#include using namespace std;
const int mod = 1000000007;
int modadd(int a, int b)
int vi[1000005];
int go[75][1000005]; // 將陣列稍微開大以避免越界,小的一維盡量定義在前面
int sum[75][1000005];
int main()
for (int i = 1; i <= n; ++i)
//int logn = 31 - __builtin_clz(n); // 乙個快捷的取對數的方法
int logn = 65;
for (int i = 1; i <= logn; ++i)
} long long m;
scanf("%lld", &m);
int ans = 0;
int curx = 1;
for (int i = 0; m; ++i)
} printf("%d\n", ans);
}
這題的 \(m≤10^\) ,雖然看似恐怖,但是實際上只需要預處理出 65 以內的 i ,就可以輕鬆解決,比起暴力列舉快了很多。用行話講,這個做法的時間複雜度是預處理 o(nlogm) ,查詢每次 o(logm) 。
倍增除了作為一種獨立的思想以外,還經常被應用到各種演算法裡面,例如 快速冪、lca 和 rmq 問題 。
總結: 這就是倍增預處理出以二的整數次冪為單位的資訊:
*在遞推中,如果狀態空間很大,可以通過成倍增長的方式,只遞推出狀態空間在2的整數次冪的值作為代表。
*每個數都可以表示成二進位制的形式,可用之前的求出的代表值拼成所需的值。
*要求這個遞推問題的狀態空間關於2的次冪具有可劃分性。
注意:為了保證統計的時候不重不漏,一般預處理出左閉右開的點權和。
題目描述
給定乙個長度為n的序列ai,定義a[i]為第i個元素的價值。現在需要找出序列中最有價值的「段落」。段落的定義是長度在[s,t]之間的連續序列。最有價值段落是指平均值最大的段落,段落的平均值=段落總價值/段落長度。
輸入輸出格式
輸入格式:
第一行乙個整數n,表示序列長度。
第二行兩個整數s和t,表示段落長度的範圍,在[s,t]之間。
第三行到第n+2行,每行乙個整數表示每個元素的價值指數。
乙個實數,保留3位小數,表示最優段落的平均值。
輸入輸出樣例
輸入樣例#1:
32 23-1
2輸出樣例#1:
1.000
資料範圍
對於30%的資料有n<=1000。
對於100%的資料有n<=100000,1<=s<=t<=n,-10000<=元素價值<=10000。
解題思路
可以看出所求問題的答案具有單調性,考慮二分答案,變為判定性問題。
對於可能的答案k,設b[i]=a[i]-k。
就是求b陣列一段長度在s,t之間的和的最大值,判斷其是否大於等於0。求區間和的最大值可以用字首和+單調佇列維護。
具體過程,貼乙份洛谷題解的圖:
時間複雜度
假設資料範圍為a,則二分答案是o(loga)的,判斷一次用了字首和和單調佇列,複雜度是o(n)的,總時間複雜度為o(nloga)。
實現**
#include using namespace std;
int n,s,t,a[100005],q[100005];//q陣列用來記錄字首和的下標
double sum[100005]; //字首和記錄到第i個的減去平均值的和
int check(double m)
return 0;
}int main()
double l=-10005,m,r=10005;
while(l+1e-5
cout <
return 0;
}
對資訊學競賽中除錯方法的建議
資訊學之於其他競賽學科的不同,就在於需要通過寫程式來表達自己的思維和想法。如何盡可能又快又好地除錯程式,成了我們必須要思考的問題。相信很多同學都有過這樣的經歷 思考乙個演算法只花了半個小時,但是把這個演算法寫對卻花了一天。思考與實現的時間往往不成正比。關於除錯有乙個大前提,就是思考的方向一定得嚴謹正...
生物資訊學中重要的評價競賽
生物資訊學是一門發展非常迅速的新興學科,其分析軟體和流程在不斷完善當中。業內經常舉辦一些 競賽 組織者獲得一些問題的金標準 真相 如組裝乙個基因組或評估變異檢測流程的準確性等,然後邀請業界內成員在一定時限內來競爭解決這些問題。通過比較基於不同方法的結果,組織者可以評估每一種方法的效能,即真陽性和假陽...
對資訊學競賽中除錯方法的建議
自 資訊學之於其他競賽學科的不同,就在於需要通過寫程式來表達自己的思維和想法。如何盡可能又快又好地除錯程式,成了我們必須要思考的問題。相信很多同學都有過這樣的經歷 思考乙個演算法只花了半個小時,但是把這個演算法寫對卻花了一天。思考與實現的時間往往不成正比。關於除錯有乙個大前提,就是思考的方向一定得嚴...