C104【模板】整体二分+树状数组 P3834 可持久化线段树2
视频链接:C104【模板】整体二分+树状数组 P3834 可持久化线段树2_哔哩哔哩_bilibili
C50【模板】可持久化线段树(主席树)P3834 静态区间第 k 小 - 董晓 - 博客园 (cnblogs.com)
// 整体二分+树状数组 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]); }