题解 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);
}

浙公网安备 33010602011771号