• 莫队


    莫队
    发明者莫涛,用于比较暴力的进行区间操作
    以简短的框架、简单易记的板子和优秀的复杂度闻名于世
    然而由于莫队算法应用的毒瘤,很多可做的莫队模板题都有着较高的难度评级,令很多初学者望而却步
    然而,如果真正理解了莫队的算法原理,那么用起来还是很简单的

    前置芝士

    莫队算法还是比较独立的。不过你还是得了解了解以下的一些知识:
    1、分块的基本思想
    2、(STL)(sort) 的用法(手写 (cmp) 函数或重载运算符实现结构体的多关键字排序)
    3、各种卡常技巧
    4、倍增/树剖 求LCA(树上莫队所需)
    5、数值离散化(用于应付很多题目)

    基础实现

    莫队算法优化的核心是分块和排序
    我们将大小为 (n) 的序列分为 (sqrt{n}) 个块,从 (1)(sqrt{n}) 编号,然后根据这个对查询区间进行排序
    一种方法是把查询区间按照左端点所在块的序号排个序,如果左端点所在块相同,再按右端点排序
    排完序后我们再进行左右指针跳来跳去的操作,虽然看似没多大用,但带来的优化实际上极大

    int Cmp(node x,node y){return x.l/sqrtn==y.l/sqrtn?x.r<y.r:x.l/sqrtn<y.l/sqrtn;}
    

    关于左右指针的移动,要确定当前的答案是否已经被统计,是否要保留

    for(rr int i=1;i<=q;i++){
    	while(L>Ask[i].l){Add(--L);}
            while(R<Ask[i].r){Add(++R);}
    	while(L<Ask[i].l){Del(L++);}
    	while(R>Ask[i].r){Del(R--);}
    	Res[Ask[i].id]=ans;
    }
    

    复杂度证明

    0.区间排序

    建个结构体,用 (sort) 跑一遍即可。平均复杂度 (O(nlog n))

    1.左指针的移动

    设每个块 (i) 中分布有 (x_i) 个左端点
    由于莫队的添加、删除操作复杂度为 (O(1)),那么处理块 (i) 的最坏时间复杂度是 (O(x_isqrt n)),指针跨越整块的时间复杂度为 (O(sqrt{n})),最坏需要跨越 (n)
    总复杂度 (O(sum{x_insqrt n}+nsqrt n)=O(nsqrt n))

    2.右指针的移动

    设每个块 (i) 中分布有 (x_i) 个左端点,由于左端点同块的区间右端点有序,那么对于这 (x_i) 个区间,右端点最坏只需总共 (O(n)) 的时间跳(最坏需跳完整个序列),总共 (nsqrt n)个块,总复杂度 (O(nsqrt n))
    至此可得出,莫队算法的总时间复杂度为 (O(nsqrt n)+O(nsqrt n)+O(nlog n)=O(nsqrt n))
    与排序之前相比,降低了一个根号之多,有了质的提升

    优化方式

    目前我只知道奇偶性优化,之后的学习了会再补
    由于在当前块所有的询问完成之后,按照朴素的莫队方法,处理下一个块时会将右指针移回来
    略过了中间的询问,从而造成不必要的浪费
    如果在回来的过程中顺便处理掉他们,时间复杂度就会优秀不少
    具体就是把块按奇偶分,奇数块从左到右扫,偶数块从右到左扫

    inline int Cmp(node x,node y){return x.l/sqrtn==y.l/sqrtn?((x.l/sqrtn)&1)?x.r<y.r:x.r>y.r:x.l/sqrtn<y.l/sqrtn;}
    

    另外,还可以用规定块长的方法优化,但是并不稳定

    例题

    [国家集训队]小Z的袜子
    小B的询问
    数列找不同

    关于莫队还有树上莫队,带修莫队什么的
    还没学,学了之后会补

  • 相关阅读:
    做好最后的1%
    南海城市大脑二期测试的思考
    职场动物进化手册
    搜索框测试用例
    三月版三一金票需求测试总结
    “魔鬼”隐藏在细节中
    java----jdbcTemplate
    内网隧道与SOCKS代理思路总结
    一些免杀方法测试
    JavaScript 关于闭包、同步、异步问题
  • 原文地址:https://www.cnblogs.com/KnightL/p/14130517.html
Copyright © 2020-2023  润新知