树状数组套线段树
https://www.luogu.com.cn/problem/P3380
题意
维护数列,支持以下操作:
-
\(op=1\),查询 \(k\) 在 \([l,r]\) 内的排名.
-
\(op=2\),查询区间内排名为 \(k\) 的值.
-
\(op=3\),修改某一位置的值.
-
\(op=4\),查询 \(k\) 在 \([l,r]\) 的前驱.
-
\(op=5\),查询 \(k\) 在 \([l,r]\) 的后继.
\(1\le n,q \le 10^5\).
思路
参考主席树,使用树状数组套线段树维护.
记数组长度为 \(n\),离散化后值域为 \(m\),询问次数为 \(q\).
各个函数:
-
\(update\):对单棵线段树单点修改,时间复杂度 \(\mathcal{O}(\log m)\).
-
\(build\):第 \(i\) 棵线段树维护 \([i-lowbit(i)+1,i]\) 区间信息,暴力 \(update\),时间复杂度 \(\mathcal{O}(n\log n \log m)\),可以用线段树合并优化到 \(\mathcal{O}(n\log m)\).
-
\(query\_range\):查询下标在 \([l,r]\),值域在 \([x,y]\) 范围的元素数量,实现依赖于两个子函数:
-
\(query\_pre\):查询单棵线段树中,值域在 \([1,x]\) 范围内的元素数量,时间复杂度 \(\mathcal{O}(\log m)\).
-
\(query\):查询下标在 \([1,pos]\),值域在 \([x,y]\) 范围内的元素数量. 从 \(pos\) 开始不断减 \(lowbit\),把下标范围内所有线段树根节点取出,累加 \(query\_pre(rt,y)-query\_pre(rt,x-1)\),时间复杂度 \(\mathcal{O}(\log n\log m)\).
返回 \(query(r,x,y)-query(l-1,x,y)\) 即可,时间复杂度和 \(query\) 相同,\(\mathcal{O}(\log n\log m)\).
-
-
\(change\):将 \(pos\) 位元素从 \(val\) 改成 \(nval\),从 \(pos\) 开始不断加 \(lowbit\),\(update\) 所有线段树即可,时间复杂度 \(\mathcal{O}(\log n\log m)\).
-
\(queryk\):查询下标在 \([l,r]\) 范围内第 \(k\) 个元素,这和主席树查询区间第 \(k\) 小逻辑是一样的,区别在于主席树只需要维护 \(l-1\) 和 \(r\) 的线段树根,这里需要维护所有 \(l-1\) 和 \(r\) 向下不断减 \(lowbit\) 的线段树根,需要精细实现避免反复开 \(vector\),时间复杂度 \(\mathcal{O}(\log n\log m)\).
空间复杂度理论宽松上界是 \(\mathcal{O}((n+q)\log n\log m)\).
得到了该数据结构,解决原问题是容易的.
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct FenwickSegmentTree{
struct node{
int cnt = 0;
int left=-1,right=-1;
};
int n,m;
vector<node> seg;
vector<int> root;
FenwickSegmentTree(int _n,int _m){
n = _n;
m = _m;
root.resize(n+1);
for (int i=1;i<=n;i++){
seg.emplace_back();
root[i] = seg.size()-1;
}
}
void update(int rt,int l,int r,int pos,int val){
if (l==r){
seg[rt].cnt += val;
return;
}
int mid = l+r >> 1;
if (pos<=mid){
if (seg[rt].left==-1){
seg.emplace_back();
seg[rt].left = seg.size()-1;
}
update(seg[rt].left,l,mid,pos,val);
}
else{
if (seg[rt].right==-1){
seg.emplace_back();
seg[rt].right = seg.size()-1;
}
update(seg[rt].right,mid+1,r,pos,val);
}
seg[rt].cnt = (seg[rt].left==-1?0:seg[seg[rt].left].cnt)+(seg[rt].right==-1?0:seg[seg[rt].right].cnt);
}
void update(int rt,int pos,int val){
if (pos<1 || pos>m) return;
update(rt,1,m,pos,val);
}
void build(vector<int>& a){
for (int i=1;i<=n;i++){
for (int j=i;j<=n;j+=j&-j){
update(root[j],a[i],1);
}
}
}
void change(int pos,int val,int nval){
for (int i=pos;i<=n;i+=i&-i){
update(root[i],val,-1);
update(root[i],nval,1);
}
}
int query_pre(int rt,int l,int r,int pos){
if (rt==-1) return 0;
if (l==r){
return seg[rt].cnt;
}
int mid = l+r >> 1;
if (pos<=mid){
return query_pre(seg[rt].left,l,mid,pos);
}
else{
return (seg[rt].left==-1?0:seg[seg[rt].left].cnt)+query_pre(seg[rt].right,mid+1,r,pos);
}
}
int query_pre(int rt,int val){
if (val<1 || val>m) return 0;
return query_pre(rt,1,m,val);
}
int query(int pos,int x,int y){
if (x>y) return 0;
ll res = 0;
for (int i=pos;i>=1;i-=i&-i){
res += query_pre(root[i],y)-query_pre(root[i],x-1);
}
return res;
}
int query_range(int l,int r,int x,int y){
return query(r,x,y)-query(l-1,x,y);
}
void push(vector<int>& vec,int op){
for (auto& rt:vec){
if (rt==-1) continue;
rt = op==0?seg[rt].left:seg[rt].right;
}
}
int queryk(vector<int>& rtl,vector<int>& rtr,int l,int r,ll k){
if (l==r){
return l;
}
int mid = l+r >> 1;
ll cnt = 0;
for (auto& rt:rtr){
if (rt!=-1 && seg[rt].left!=-1){
cnt += seg[seg[rt].left].cnt;
}
}
for (auto& rt:rtl){
if (rt!=-1 && seg[rt].left!=-1){
cnt -= seg[seg[rt].left].cnt;
}
}
if (cnt>=k){
push(rtl,0);
push(rtr,0);
return queryk(rtl,rtr,l,mid,k);
}
else{
push(rtl,1);
push(rtr,1);
return queryk(rtl,rtr,mid+1,r,k-cnt);
}
}
int queryk(int l,int r,ll k){
if (l<1 || l>n || r<1 || r>n || l>r || k<1) return -1;
vector<int> rtl,rtr;
ll cnt = 0;
for (int i=r;i>=1;i-=i&-i){
rtr.push_back(root[i]);
cnt += seg[root[i]].cnt;
}
for (int i=l-1;i>=1;i-=i&-i){
rtl.push_back(root[i]);
cnt -= seg[root[i]].cnt;
}
if (cnt<k) return -1;
return queryk(rtl,rtr,1,m,k);
}
};
const int INF = 2147483647;
void solve(){
int n,q;
cin >> n >> q;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
}
auto uni = a;
vector<array<int,5>> que(q+1);
for (int i=1;i<=q;i++){
int op,l,r,pos,k;
cin >> op;
if (op==3){
cin >> pos >> k;
}
else{
cin >> l >> r >> k;
}
if (op!=2){
uni.push_back(k);
}
que[i] = {op,l,r,pos,k};
}
sort(uni.begin()+1,uni.end());
uni.erase(unique(uni.begin()+1,uni.end()),uni.end());
int m = uni.size()-1;
for (int i=1;i<=n;i++){
a[i] = lower_bound(uni.begin()+1,uni.end(),a[i])-uni.begin();
}
for (int t=1;t<=q;t++){
auto& [op,l,r,pos,k] = que[t];
if (op!=2){
k = lower_bound(uni.begin()+1,uni.end(),k)-uni.begin();
}
}
FenwickSegmentTree fsg(n,m);
fsg.build(a);
for (int t=1;t<=q;t++){
auto& [op,l,r,pos,k] = que[t];
if (op==1){
cout << fsg.query_range(l,r,1,k-1)+1 << '\n';
}
else if (op==2){
cout << uni[fsg.queryk(l,r,k)] << '\n';
}
else if (op==3){
fsg.change(pos,a[pos],k);
a[pos] = k;
}
else if (op==4){
ll cnt = fsg.query_range(l,r,1,k-1);
int res = fsg.queryk(l,r,cnt);
if (res==-1){
cout << -INF << '\n';
}
else{
cout << uni[res] << '\n';
}
}
else{
ll cnt = fsg.query_range(l,r,1,k);
int res = fsg.queryk(l,r,cnt+1);
if (res==-1){
cout << INF << '\n';
}
else{
cout << uni[res] << '\n';
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号