对权值线段树剪枝的误解--以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

相关文章
相关标签/搜索