Wavelet Matrix

摸个鱼。英语课上研究了一会感觉挺好玩的。

一种基于二进制分解的能在 \(O(\log w)\) 时间内解决静态区间 k 小、区间排名之类的问题的简单数据结构,貌似能套个平衡树拓展到动态。

初始化

从高到低对每一个二进制位进行以下操作:

在矩阵对应行记录一下数组每个数这一位的值。然后将数组按照这一位稳定排序,记录一下 0 和 1 的分割线位置。

(感觉此处有个图能讲得清楚,不过咕)

前缀和也会用到所以预处理下前缀和。不考虑时空常数的话代码就四行:

for(int j=30;~j;--j){
    for(int i=1;i<=n;++i)s[j][i]=s[j][i-1]+(a[i]>>j&1);
    pos[j]=stable_partition(a+1,a+n+1,[&](int x){return !(x>>j&1);})-a;
}

区间 k 小

首先看第一行没有排过序的,假如区间内 0 的数量(这里前缀和就有用了) \(\ge k\) 那答案这一位肯定是 0,否则是 1。然后由于稳定排序 0 和 1 内部的相对位置固定,当前区间只对应下一层的两个区间,且答案在哪个区间已经知道,所以一直往下走即可。

时空和码量都比主席树优秀。

int kth(int l,int r,int k){
    int ret=0;
    for(int j=30;~j;--j){
        int c=s[j][r]-s[j][l-1];
        if(r-l+1-c>=k){
            l-=s[j][l-1];
            r-=s[j][r];
        }
        else{
            k-=r-l+1-c;
            l=pos[j]+s[j][l-1];
            r=pos[j]+s[j][r]-1;
            ret|=1<<j;
        }
    }
    return ret;
}

区间排名

刚用到了先放一下。

用来实现静态在线矩形数点。以下代码返回 \([l,r]\) 之间 \(<x\) 的数的个数。

int rk(int l,int r,int x){
    int ret=0;
    for(int j=19;~j;--j){
        if(x>>j&1){
            ret+=r-l+1-s[j][r]+s[j][l-1];
            l=pos[j]+s[j][l-1];
            r=pos[j]+s[j][r]-1;
        }
        else{
            l-=s[j][l-1];
            r-=s[j][r];
        }
    }
    return ret;
}

posted @ 2024-02-27 20:21  KnownError  阅读(158)  评论(0)    收藏  举报