C108 整体二分+树状数组(区修+区查)P3332 [ZJOI2013] K大数查询

视频链接:C108 整体二分+树状数组(区修+区查)P3332 [ZJOI2013] K大数查询_哔哩哔哩_bilibili

 

 

 

参考:C82 树状数组 区修+区查 P3372 线段树1 - 董晓 - 博客园 (cnblogs.com)

Luogu P3332 [ZJOI2013] K大数查询

复制代码
// 整体二分+树状数组(区修+区查)O(n*logn*logn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N=50005;
int n,m,cnt,ans[N];
struct Q{
  //查询: opt=2,[x,y]第k大,id编号
  //插入: opt=1,[x,y]加入k
  int opt,x,y,id; LL k;
}q[N],q1[N],q2[N];

struct BIT{ //树状数组
  LL s[N];
  void add(int x,int v){ //点修
    for(int i=x;i<=n;i+=i&-i)s[i]+=v;
  }
  LL sum(int x){ //前缀和
    LL t=0;
    for(int i=x;i;i-=i&-i)t+=s[i];
    return t;
  }
}a,b; //a:di的区间和, b:di*(i-1)的区间和

void solve(int l,int r,int L,int R){
  if(l>r)return;
  if(L==R){
    for(int i=l;i<=r;i++)ans[q[i].id]=L; return;
  }
  int mid=(L+R)>>1;    //二分值域
  int p1=0,p2=0;       //分流操作
  for(int i=l;i<=r;i++){ //枚举操作
    int x=q[i].x,y=q[i].y;
    if(q[i].opt==1){   //插入
      if(q[i].k>mid)   //若k值大,则维护贡献
        a.add(x,1),a.add(y+1,-1),
        b.add(x,x-1),b.add(y+1,-y),
        q2[++p2]=q[i]; //分流到右边
      else q1[++p1]=q[i];
    } 
    else{ //查询
      LL s=a.sum(y)*y-b.sum(y)
         -(a.sum(x-1)*(x-1)-b.sum(x-1));
      if(s>=q[i].k)    //若s太多,则第k大在右边
        q2[++p2]=q[i]; //分流到右边
      else q[i].k-=s,q1[++p1]=q[i];
    }
  }
  for(int i=1;i<=p2;i++) //清空贡献
    if(q2[i].opt==1){
      int x=q2[i].x,y=q2[i].y;
      a.add(x,-1),a.add(y+1,1),
      b.add(x,-x+1),b.add(y+1,y);
    }
  for(int i=1;i<=p1;i++) q[l+i-1]=q1[i];
  for(int i=1;i<=p2;i++) q[l+p1+i-1]=q2[i];
  solve(l,l+p1-1,L,mid);
  solve(l+p1,r,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++){ //操作信息
    scanf("%d%d%d%lld",&q[i].opt,&q[i].x,&q[i].y,&q[i].k);
    if(q[i].opt==2) q[i].id=++cnt;
  }
  solve(1,m,-n,n); //整体二分
  for(int i=1;i<=cnt;i++)printf("%d\n",ans[i]);
}
复制代码

 

复制代码
// 整体二分+线段树 O(n*logn*logn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
#define lc u<<1
#define rc u<<1|1
const int N=50005;
int n,m,cnt,ans[N];
struct Q{
  //查询: opt=2,[x,y]第k大,id编号
  //插入: opt=1,[x,y]加入k
  int opt,x,y,id; LL k;
}q[N],q1[N],q2[N];

struct Tree{ //线段树
  int l,r;
  LL sum,tag; //区间和,懒标记
}tr[N<<2];

void pushup(int u){ //上传信息
  tr[u].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int u){ //下传信息
  if(!tr[u].tag) return;
  int l=tr[u].l,r=tr[u].r,mid=(l+r)>>1;
  tr[lc].tag+=tr[u].tag;
  tr[rc].tag+=tr[u].tag;
  tr[lc].sum+=tr[u].tag*(mid-l+1);
  tr[rc].sum+=tr[u].tag*(r-mid);
  tr[u].tag=0;
}
void build(int u,int l,int r){ //建树
  tr[u]={l,r,0,0};
  if(l==r) return;
  int mid=(l+r)>>1;
  build(lc,l,mid);
  build(rc,mid+1,r);
}
void change(int u,int x,int y,LL v){ //区修
  int l=tr[u].l,r=tr[u].r;
  if(x<=l&&r<=y){
    tr[u].tag+=v;
    tr[u].sum+=v*(r-l+1);
    return;
  }
  pushdown(u);
  int mid=(l+r)>>1;
  if(x<=mid) change(lc,x,y,v);
  if(mid<y) change(rc,x,y,v);
  pushup(u);
}
LL query(int u,int x,int y){ //区查
  int l=tr[u].l,r=tr[u].r;
  if(x<=l&&r<=y) return tr[u].sum;
  pushdown(u);
  int mid=(l+r)>>1;LL s=0;
  if(x<=mid) s+=query(lc,x,y);
  if(mid<y) s+=query(rc,x,y);
  return s;
}
void solve(int l,int r,int L,int R){
  if(l>r) return;
  if(L==R){
    for(int i=l;i<=r;i++)ans[q[i].id]=L;
    return;
  }
  int mid=(L+R)>>1;   //二分值域
  int p1=0,p2=0;      //分流操作
  for(int i=l;i<=r;i++){ //枚举操作
    if(q[i].opt==1){  //插入
      if(q[i].k>mid)  //若k值大,则维护贡献
        change(1,q[i].x,q[i].y,1),
        q2[++p2]=q[i]; //分流到右边
      else q1[++p1]=q[i];
    }
    else{ //查询
      LL s=query(1,q[i].x,q[i].y);
      if(s>=q[i].k)    //若s太多,则第k大在右边
        q2[++p2]=q[i]; //分流到右边
      else q[i].k-=s,q1[++p1]=q[i];
    }
  }
  for(int i=1;i<=p2;i++) //清空贡献
    if(q2[i].opt==1)change(1,q2[i].x,q2[i].y,-1); 
  for(int i=1;i<=p1;i++) q[l+i-1]=q1[i];
  for(int i=1;i<=p2;i++) q[l+p1+i-1]=q2[i];
  solve(l,l+p1-1,L,mid);
  solve(l+p1,r,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m);
  build(1,1,n);
  for(int i=1;i<=m;i++){ //操作信息
    scanf("%d%d%d%lld",&q[i].opt,&q[i].x,&q[i].y,&q[i].k);
    if(q[i].opt==2) q[i].id=++cnt;
  }
  solve(1,m,-n,n); //整体二分
  for(int i=1;i<=cnt;i++)printf("%d\n",ans[i]);
}
复制代码

  

posted @   董晓  阅读(164)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示