zoukankan      html  css  js  c++  java
  • 【莫队·进阶篇】二次离线莫队与区间逆序对问题

    (Preface)

    一种高端科技,在NOI2020Day1T3爆炸之后,不会(A)部分分(裸·区间逆序对)的余下定决心要好好学习根号算法。

    二次离线莫队听名字就是个非常高级的东西,而且虽说是二次离线,但它的复杂度仍旧是(O(nsqrt n)),非常神奇。

    应用范围

    一般用于移动左右端点时复杂度较大的莫队。

    例如区间逆序对,它有一个非常显然的莫队+树状数组的(O(nsqrt nlogn))做法,原因就是每次移动端点时要在树状数组上询问。

    而二次离线莫队可以完美解决这个问题。

    以区间逆序对问题为例

    假设当前左右端点为(L,R),现要将右端点向右移一位。

    考虑这次移动之后,答案的变化值就是([L,R])中大于(a_{R+1})的数的个数。

    这东西可以拆成两个:([1,R])中大于(a_{R+1})的数的个数减去([1,L-1])中大于(a_{R+1})的数的个数。

    其中前面那个东西显然可以树状数组(O(nlogn))预处理。

    而后面这个东西,我们可以对于每个位置开一个(vector),然后把(R+1)连同当前询问的编号一同扔到(L-1)(vector)中。

    根据莫队的复杂度证明,我们一共会移动端点(O(nsqrt n))次,这样的内存可能会有点大。

    然而我们发现,假设一个询问需要我们把(R)向右移到(R'),移动过程中(L)是保持不动的,因此实际上我们可以直接把([R+1,R'])整个区间连同询问编号一起扔到(L-1)(vector)中。这样一来内存就是(O(n))级别的了。

    把移动区间扔到(L-1)(vector)中的过程,就是第二次离线。

    然后我们只要从左到右枚举每一个位置,处理该位置(vector)中的询问。

    注意虽然我们存的是区间,但询问时依旧要枚举区间中的每一个数一个个询问,询问次数依旧是(O(nsqrt n))的。

    又由于我们只会枚举(1sim n)(n)个位置,因此可以考虑适当增加修改的复杂度以实现(O(1))询问。

    于是就想到(O(nsqrt n))修改、(O(1))询问的分块。

    这样就彻底解决了整个问题。

    具体实现过程(伪代码?)

    具体实现中,由于左端点移动和右端点移动答案变化值询问范围相反,因此很多东西都要开两个。

    • 读入数据并进行一些简单处理(例如离散化)。
    • 预处理:(t1_i,t2_i)分别表示(1sim i-1)中大于/小于(i)的数的个数,则用树状数组预处理出它俩的前缀和(s1_i,s2_i)。((t1_i,t2_i)无需存储)
    • 第一次离线: 给询问排序,进行莫队。((g1[x],g2[x])为每个位置的两个(vector),四元组表示询问左/右端点、询问编号以及对答案贡献的符号)
      • (R<r_i):往(g1[L-1])中扔入((R+1,r_i,pos_i,-1))(ans[pos_i])加上(s1_{r_i}-s1_R),然后令(R=r_i)
      • (L>l_i):往(g2[R])中扔入((l_i,L-1,pos_i,1))(ans[pos_i])减去(s2_{L-1}-s2_{l_i-1}),然后令(L=l_i)
      • (R>r_i):往(g1[L-1])中扔入((R_i+1,R,pos_i,1))(ans[pos_i])减去(s1_R-s1_{r_i}),然后令(R=r_i)
      • (L<l_i):往(g2[R])中扔入((L,l_i-1,pos_i,-1))(ans[pos_i])加上(s2_{l_i-1}-s2_{L-1}),然后令(L=l_i)
    • 第二次离线: 枚举每一个位置,处理(vector)中的询问。
      • (O(sqrt n))加入(a_i)的贡献。
      • 枚举(g1)中的元素((l,r,pos,op)),枚举(xin[l,r]),给(ans[pos])加上(op imes Q1(a_x+1))(Q1(v))返回大于等于(v)的数的个数)。
      • 枚举(g2)中的元素((l,r,pos,op)),枚举(xin[l,r]),给(ans[pos])加上(op imes Q2(a_x-1))(Q2(v))返回小于等于(v)的数的个数)。
    • 最后,给答案做个前缀和(因为我们求出的只是答案变化值),然后输出。

    时间复杂度分析

    预处理(树状数组):(O(nlogn))

    第一次离线(莫队):(O(n))(因为此时我们没必要再一点一点移动端点,直接一步到位就可以了)

    第二次离线(分块):(O(nsqrt n))(修改单次复杂度(O(sqrt n)),次数(O(n));询问单次复杂度(O(1)),次数(O(nsqrt n))

    总复杂度:(O(nsqrt n))

    (如有不对请指出~~~)

    (Postscript)

    毒瘤lxl的题目让我感受到深深的恶意,果然NOI这种东西还不是余现在的水平能够挑战的。。。

  • 相关阅读:
    HDU 1525 Euclid's Game 博弈
    HDU 1272 小希的迷宫 并查集
    HDU1232 畅通工程 并查集
    HDU1760 A New Tetris Game NP态
    HDU5795A Simple Nim SG定理
    UVA10561 Treblecross 组合游戏/SG定理
    HDU1848 Fibonacci again and again SG函数
    HDU1532 Drainage Ditches 网络流EK算法
    树状数组小结
    HDU3333 Turing Tree 树状数组+离线处理
  • 原文地址:https://www.cnblogs.com/Nero-Claudius/p/MoQueue1.html
Copyright © 2011-2022 走看看