首页 > 其他分享 >C108 整体二分+树状数组(区修+区查)P3332 [ZJOI2013] K大数查询

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

时间:2024-03-30 20:34:12浏览次数:24  
标签:P3332 区修 int 区查 LL tr ++ tag include

视频链接: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]);
}

  

标签:P3332,区修,int,区查,LL,tr,++,tag,include
From: https://www.cnblogs.com/dx123/p/18100565

相关文章

  • 2024-1-24案例(地区查询)以及遍历方法
    目录案例(地区查询)步骤解析案例里面的map方法该案例的最后一个将数据插入到页面上案例(地区查询)需求:根据输入的省份名字和城市名字,查询地区并渲染列表步骤首先:确定URL网址和参数说明查询某个省内某个城市的所有地区参数名:pname:省份名字或直辖市名字,比如北京、福建省、辽......
  • docker中的mysql时区修改
    永久修改进入容器dockerexec-itmysql5.7bash查看当前时区date-R修改时区cp/usr/share/zoneinfo/PRC/etc/localtime#或者ln-sf/usr/share/zoneinfo/Asia/Shanghai/etc/localtime#退出exit#重启容器生效dockerrestartmysql5.7临时修改-重启失......
  • Docker内时区查询和修改方法
    利用【dockerexec-it容器ID/bin/bash】命令进入Docker容器内,执行【date】命令查看Docker容器的时间发现与宿主机有误差时,修改时间和时区。方法一:在【宿主机】中执行命令,【dockercp/etc/localtime容器ID:/etc/localtime】,重启Docker容器。方法二:在【宿主机】中执行命......
  • Hive表分区查询show partitions tablename
    Hive表分区查询showpartitionstablenameSparkSql:%sqlshowpartitionsgrainfo;......
  • 时区修改
    1.查询当前系统信息>cat/etc/centos-release2.查询操作系统的发行版号>uname-r3.查询当前系统时区>timedatectl|grep"Timezone"4.设置为北京时区>timedatectlset-timezoneAsia/Shanghai5.查询所有时区>timedatectllist-timezones......
  • 编辑引导扇区修复分区引导解决磁盘分区打不开
    关键词:raw格式 数据错误循环冗余错误  编辑引导扇区 修复分区引导问题描述:E盘双击打不开,提示是否将其格式化,点取消,提示数据错误(循环冗余错误)。计算机-管理-磁盘管理显示格式为raw格式。系统变得很卡很卡。。。解决过程:1:用磁盘精灵DiskGenius-坏道检测与修复-开始检测,检测结......
  • 记录liunx服务器和docker时区修改
    记录服务器和docker时区修改前言我的博客是部署在docker里面的,然后我发现评论和留言的时间和北京时间是有差别的,相差8个小时,然后发现是因为容器中的时区设置与服务器是不一致的,所以需要设置一下。更改liunx服务器时区查看当前时区设置使用date命令查看当前系统时间,发现当前......
  • 第七届河南省赛 zzuoj 10403: D.山区修路 (DP转换&&技巧)
    10403:D.山区修路TimeLimit: 2Sec  MemoryLimit: 128MBSubmit: 68  Solved: 22[Submit][Status][WebBoard]Description某山区的孩子们上学必须经过一条凹凸不平的土路,每当下雨天,孩子们非常艰难。现在村里走出来的Dr.Kong决定募捐资金重新修建着条路......
  • docker 容器内系统时区tomcat时区修改
    现象:查看docker容器运行的项目的日志时发现时间与北京时间差8小时原因:很容易猜到是容器时区错误,使用的是协调世界时UTC,可以近似看作0时区,我们中国应该使用......