可持久化线段树(主席树)

发布时间 2023-12-10 02:16:55作者: potential-star

给定 n 个整数构成的序列 a,将对于指定的闭区间 [l,r] 查询其区间内的第 k 小值。

  • 题目一开始的离散化复杂度为\(O(n\log n)\),构建基础主席树复杂度为\(O(n\log n)\),统计并插入的复杂度是\(O(n\log n + n\log n)=O(n\log n)\),询问的复杂度是\(O(m\log n)\)。复杂度总和就是\(O((m+n)\log n)\)
#define N 200005
#define lc(x) tr[x].l
#define rc(x) tr[x].r
struct node{
  int l,r,s; //s:节点值域中有多少个数
}tr[N*20];
int root[N],idx;
int n,m,a[N];
vector<int> b;

void build(int &x,int l,int r){
  x=++idx;
  if(l==r) return;
  int m=l+r>>1;
  build(lc(x),l,m);
  build(rc(x),m+1,r);
}
void insert(int x,int &y,int l,int r,int k){
  y=++idx; tr[y]=tr[x]; tr[y].s++;
  if(l==r) return;
  int m=l+r>>1;
  if(k<=m) insert(lc(x),lc(y),l,m,k);
  else insert(rc(x),rc(y),m+1,r,k);
}
int query(int x,int y,int l,int r,int k){
  if(l==r) return l;
  int m=l+r>>1;
  int s=tr[lc(y)].s-tr[lc(x)].s;
  if(k<=s) return query(lc(x),lc(y),l,m,k);
  else return query(rc(x),rc(y),m+1,r,k-s);
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1; i<=n; i++){
    scanf("%d",&a[i]); b.push_back(a[i]);
  }
  sort(b.begin(),b.end());
  b.erase(unique(b.begin(),b.end()),b.end());
  int bn=b.size();

  build(root[0],1,bn);
  for(int i=1; i<=n; i++){
    int id=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
    insert(root[i-1],root[i],1,bn,id);
  }
  while(m--){
    int l,r,k; scanf("%d%d%d",&l,&r,&k);
    int id=query(root[l-1],root[r],1,bn,k)-1;
    printf("%d\n",b[id]);
  }
  return 0;
}