题解 SP3266 【KQUERY - K-query】

两个 \(\log\) 的树套树 /fad

众所周知,平衡树可以用vector水过,所以我用的是线段树套vector

Solution

树套树(如线段树套平衡树),其实就是把线段树的每一棵子树都当作一棵平衡树。这样,当我们区间查询时,若范围包括一棵子树,就可以直接利用其信息了。那么套vector也一样,把每个子树都当成一个维护从小到大顺序的vector即可。

建树

对于每个数,从上到下依次插入 \(vector\) 即可,和常规线段树建树差不多

复杂度 \(\mathcal O(n\log ^2n)\)

#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define upp(x,y) (upper_bound(v[x].begin(),v[x].end(),y))
//同一个vector内二分
#define ins(x,y) {v[x].insert(upp(x,y),y);}
//在第x个vector插入y
int n,m,a[N],ans;
vector<int>v[N*4];
inline void build(int p,int k,int x=1,int l=1,int r=n)
{
    ins(x,k);
    if(l==r)return;
    int mid=l+r>>1;
    if(p<=mid)build(p,k,ls(x),l,mid);
    else build(p,k,rs(x),mid+1,r);
}
inline void init()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        build(i,a[i]);
    }m=read();
}

查询 \(k\) 在区间内的排名

此操作等价于区间内不比 \(k\) 大的数的数量+1,那找到比 \(k\) 小的有多少即可

对于完整的子树,我们在其 \(vector\) 上二分出\(k\)的位置

对于不完整的子树,需分三种情况继续递归下去:完全在左子树、完全在右子树、分跨两子树。若分跨两子树,我们分左右递归两次即可。

然后输出区间长度减去排名就好啦~

复杂度 \(\mathcal O(\log^2n)\)

#define Rank(x,y) (upp(x,y)-v[x].begin())
//在第x个vector内二分出y的排名
int rk(int k,int l,int r,int x=1,int L=1,int R=n)
{
    if(l==L&&r==R)return Rank(x,k);
    int mid=L+R>>1;
    if(r<=mid)return rk(k,l,r,ls(x),L,mid);
    else if(l>mid)return rk(k,l,r,rs(x),mid+1,R);
    else return rk(k,l,mid,ls(x),L,mid)+rk(k,mid+1,r,rs(x),mid+1,R);
}
posted @ 2020-10-24 19:24  ADay526  阅读(123)  评论(0)    收藏  举报