對權值線段樹剪枝的誤解--以HDU6703爲例

引子

對hdu6703,首先將問題轉化爲「詢問一個排列中大於等於k的值裏,下標超過r的最小權值是多少」
咱們採用官方題解中的作法:權值線段樹+剪枝
對(a[i],i)建線段樹,查詢權值線段樹的[k,n]中第一個下標超過r的值
代碼是這樣的spa

int ask(int l, int r, int root){
    int mid = (l+r)>>1;
    if(mx[root]<=R)return -1;
    if(l==r){return l;}
    int ans = -1;
    if(k<=mid)ans=ask(lson);
    if(ans==-1)ans=ask(rson);
    return ans;
}

這把辣雞的我給看meng了:在R=n的時候,最壞狀況下一直向左遞歸,而且沒找到而後向右遞歸,再向右遞歸的同時又重複沒找到,這個ask不就退化成O(n)的了嗎?code

思考

通過半個月的思考(被虐),我大概懂了這個剪枝
首先分析如下幾個問題:遞歸

什麼狀況下才會遞歸下去?

由代碼第三行的class

if(mx[root]<=R)return -1;

咱們能夠知道,只有當前權值區間\((l,r)\)的最大下標超過R才可能存在答案查詢

什麼狀況下會往左遞歸而且不會從左邊的遞歸返回答案?

假設當前權值區間爲\([l,r]\)
若是往左邊遞歸沒有O(1)返回的話,根據上面的結論,那麼必定是由於左區間\([l,mid]\)存在一個下標大於R
可是,左區間中合法區間應該爲\([max(k,l),mid]\)
因此當\(k>l\),且答案均分佈在\([l,k]\)時,纔會向左遞歸而且不從左區間返回答案思考

何時「錯誤的左區間遞歸」會結束?

假設最壞狀況,答案在k-1裏,k-1一直在作區間的遞歸中,只有遞歸到當l=k的時候,纔會結束這個錯誤
由線段樹的相關性質只能夠知道,這個最壞狀況能夠到\(l=r=k\),也就是跑了一個\(O(logn)\)的鏈co

深刻思考

思考完以上幾個問題,繼續思考:錯誤

當走完這條錯誤鏈,回溯的時候,會回溯到哪裏?

固然是第一次出現這個錯誤分叉的地方(其實就是父節點)
可是此時咱們在左區間沒找到答案,會去右區間,而右區間\([mid+1,r]\)是徹底包含於合法區間\([k,n]\)中的,因此只會出現兩種狀況
1.右區間沒有合法答案,O(1)退出,繼續回溯
2.右區間有答案,最終答案必在右區間中
一旦出現了2,就是正常的沒有限制\([k,n]\)的線段樹找最小值的O(logn)的作法了
而1也只是一個普通的回溯,按照父節點回溯到最原始的錯誤分叉,答案就在另外一條路中
這個問題就解決啦math

結論

這個剪枝強無敵,最終詢問操做的執行次數只有兩條鏈
複雜度爲O(logn)return

相關文章
相關標籤/搜索