• 对权值线段树剪枝的误解--以HDU6703为例


    引子

    对hdu6703,首先将问题转化为“询问一个排列中大于等于k的值里,下标超过r的最小权值是多少”
    我们采用官方题解中的做法:权值线段树+剪枝
    对(a[i],i)建线段树,查询权值线段树的[k,n]中第一个下标超过r的值
    代码是这样的

    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)的了吗?

    思考

    经过半个月的思考(被虐),我大概懂了这个剪枝
    首先分析以下几个问题:

    什么情况下才会递归下去?

    由代码第三行的

    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))的链

    深入思考

    思考完以上几个问题,继续思考:

    当走完这条错误链,回溯的时候,会回溯到哪里?

    当然是第一次出现这个错误分叉的地方(其实就是父节点)
    但是此时我们在左区间没找到答案,会去右区间,而右区间([mid+1,r])是完全包含于合法区间([k,n])中的,所以只会出现两种情况
    1.右区间没有合法答案,O(1)退出,继续回溯
    2.右区间有答案,最终答案必在右区间中
    一旦出现了2,就是正常的没有限制([k,n])的线段树找最小值的O(logn)的做法了
    而1也只是一个普通的回溯,按照父节点回溯到最原始的错误分叉,答案就在另一条路中
    这个问题就解决啦

    结论

    这个剪枝强无敌,最终询问操作的执行次数只有两条链
    复杂度为O(logn)

  • 相关阅读:
    WPF 中依赖属性的继承(Inherits)
    使用FluentValidation来进行数据有效性验证
    使用ASP.Net WebAPI构建REST服务(六)——Self-Host
    使用ASP.Net WebAPI构建REST服务(五)——客户端
    使用ASP.Net WebAPI构建REST服务(四)——参数绑定
    使用ASP.Net WebAPI构建REST服务(三)——返回值
    使用ASP.Net WebAPI构建REST服务(二)——路由
    使用ASP.Net WebAPI构建REST服务(一)——简单的示例
    WPF在代码中创建DataTemplate时候的异常
    一个简单的WeakList的实现
  • 原文地址:https://www.cnblogs.com/wrjlinkkkkkk/p/11513004.html
Copyright © 2020-2023  润新知