C104【模板】整体二分+树状数组 P3834 可持久化线段树2

视频链接:C104【模板】整体二分+树状数组 P3834 可持久化线段树2_哔哩哔哩_bilibili

 

 

 

C50【模板】可持久化线段树(主席树)P3834 静态区间第 k 小 - 董晓 - 博客园 (cnblogs.com)

Luogu P3834 【模板】可持久化线段树 2

// 整体二分+树状数组 O(n*logn*logV)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=200005;
int n,m,cnt;     //cnt:原数个数+查询个数
int ans[N],s[N]; //s:树状数组
struct Q{
  //原数: x位置,y值,k=0,id=0,opt=0
  //查询: [x,y]第k小,id编号,opt=1
  int x,y,k,id,opt;
}q[N<<1],q1[N<<1],q2[N<<1];

void add(int x,int v){ //加入贡献
  while(x<=n)s[x]+=v,x+=x&(-x);
}
int sum(int x){ //前缀和
  int t=0;
  while(x)t+=s[x],x-=x&(-x);
  return t;
}
void solve(int l,int r,int L,int R){
  if(l>r) return; //[l,r]数据个数域 [L,R]值域
  if(L==R){
    for(int i=l;i<=r;i++)
      if(q[i].opt) ans[q[i].id]=L; //记录答案
    return;
  }
  int mid=(L+R)>>1,p1=0,p2=0;
  for(int i=l;i<=r;i++){  //原数一定在查询前面
    if(!q[i].opt){        //若是原数,按值分流
      if(q[i].y<=mid)   
        add(q[i].x,1),    //加入贡献
        q1[++p1]=q[i];    //分流到左边
      else q2[++p2]=q[i]; //分流到右边
    }
    else{                 //若是查询,按个数分流
      int s=sum(q[i].y)-sum(q[i].x-1);
      if(s>=q[i].k) q1[++p1]=q[i];  //分流到左边
      else q[i].k-=s,q2[++p2]=q[i]; //分流到右边
    }
  }
  for(int i=1;i<=p1;i++)
    if(!q1[i].opt) add(q1[i].x,-1); //减去贡献
  for(int i=1;i<=p1;i++)q[i+l-1]=q1[i]; 
  for(int i=1;i<=p2;i++)q[i+l+p1-1]=q2[i]; //合并数组
  solve(l,l+p1-1,L,mid);
  solve(l+p1,r,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m);
  int mi=2e9,mx=-2e9,x,y,k;
  for(int i=1;i<=n;i++){
    scanf("%d",&x);
    q[++cnt]={i,x,0,0,0};
    mi=min(mi,x);mx=max(mx,x);
  }
  for(int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&k);
    q[++cnt]={x,y,k,i,1};
  }
  solve(1,cnt,mi,mx); //整体二分
  for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}

 

// 整体二分+树状数组 O(nlognlogV)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=200005;
int n,m,cnt;     //cnt:原数个数+查询个数
int ans[N],s[N]; //s:树状数组
struct Q{
  //查询: [x,y]第k小,id编号,opt=1  
  //原数: x位置,y值,k=0,id=0,opt=0
  int x,y,k,id,opt;
}q[N<<1],t[N<<1];

void add(int x,int v){ //加入贡献
  while(x<=n)s[x]+=v,x+=x&(-x);
}
int sum(int x){ //前缀和
  int t=0;
  while(x)t+=s[x],x-=x&(-x);
  return t;
}
void solve(int l,int r,int L,int R){
  if(l>r) return; //[l,r]数据个数域 [L,R]值域
  if(L==R){
    for(int i=l;i<=r;i++)
      if(q[i].opt) ans[q[i].id]=L; //记录答案
    return;
  }
  int mid=(L+R)>>1,p1=l-1,p2=r+1;
  for(int i=l;i<=r;i++){  //原数一定在查询前面
    if(!q[i].opt){        //若是原数,按值分流
      if(q[i].y<=mid)   
        add(q[i].x,1),    //加入贡献
        t[++p1]=q[i];    //分流到左边
      else t[--p2]=q[i]; //分流到右边
    }
    else{                 //若是查询,按个数分流
      int s=sum(q[i].y)-sum(q[i].x-1);
      if(s>=q[i].k) t[++p1]=q[i];  //分流到左边
      else q[i].k-=s,t[--p2]=q[i]; //分流到右边
    }
  }
  for(int i=l;i<=p1;i++)
    if(!t[i].opt) add(t[i].x,-1); //减去贡献
  for(int i=l;i<=p1;i++)q[i]=t[i]; 
  for(int i=p2;i<=r;i++)q[i]=t[r-i+p2];
  solve(l,p1,L,mid);
  solve(p2,r,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m);
  int mi=2e9,mx=-2e9,x,y,k;
  for(int i=1;i<=n;i++){
    scanf("%d",&x);
    q[++cnt]={i,x,0,0,0};
    mi=min(mi,x);mx=max(mx,x);
  }
  for(int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&k);
    q[++cnt]={x,y,k,i,1};
  }
  solve(1,cnt,mi,mx); //整体二分
  for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}

 

// 整体二分+树状数组 O(nlognlogV)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int N=200005;
#define lowbit(x) (x&-x)
int n,m,ans[N];

struct Q{
  //查询: [x,y]第k小,id编号,opt=1
  //原数: x位置,y值,opt=0
  int x,y,k,id,opt;
}; 
vector<Q>q; //数据序列

struct BIT{
  vector<int>s;
  void init(int n){s.resize(n);}
  void add(int x,int k){
    while(x<=n)s[x]+=k,x+=lowbit(x);
  }
  int sum(int x){
    int t=0;
    while(x)t+=s[x],x-=lowbit(x);
    return t;
  }
  int sum(int l,int r){
    return sum(r)-sum(l-1);
  }
}tree; //树状数组

void solve(vector<Q>q,int L,int R){
  if(!q.size()) return;
  if(L==R){
    for(auto i:q) if(i.opt) ans[i.id]=L;
    return; 
  }
  int mid=L+R>>1; vector<Q>q1,q2;
  for(auto i:q){
    if(!i.opt){ //若是原数,按值分流
      if(i.y<=mid)
        tree.add(i.x,1),    //加入贡献
        q1.push_back(i);    //分流到左边
      else q2.push_back(i); //分流到右边
    }
    else{      //若是查询,按个数分流
      int s=tree.sum(i.x,i.y);    
      if(s>=i.k) q1.push_back(i); //分流到左边
      else i.k-=s,q2.push_back(i);//分流到右边
    }
  }
  for(auto i:q1)
    if(!i.opt) tree.add(i.x,-1); //减去贡献
  solve(q1,L,mid); 
  solve(q2,mid+1,R); //分治
}
int main(){
  int mi=2e9,mx=-2e9,x,y,k;
  scanf("%d%d",&n,&m); 
  tree.init(n+1);
  for(int i=1;i<=n;i++)
    scanf("%d",&x), mi=min(mi,x), mx=max(mx,x), 
    q.push_back({i,x,0,0,0});
  for(int i=1;i<=m;i++)
    scanf("%d%d%d",&x,&y,&k),
    q.push_back({x,y,k,i,1});
  solve(q,mi,mx);
  for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}

 

posted @ 2024-03-22 18:49  董晓  阅读(202)  评论(0编辑  收藏  举报