BZOJ1901 Dynamic Rankings 题解(离线分治+树状数组)

博客围绕BZOJ1901题目展开,给定长度为n的序列a,支持m次操作,包括查询区间第k大数和修改序列元素。它是POJ2104的拓展,将修改操作拆分为撤销原数贡献和加入新数贡献,时间复杂度为O((n+m)loga[i]logn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:BZOJ1901.
题目大意:给定一个长度为 n n n的序列 a a a,支持 m m m次操作:
1.格式 Q   i   j   k Q\,i\,j\,k Qijk,表示查询区间 [ i , j ] [i,j] [i,j]中第 k k k大的数.
2.格式 C   i   t C\,i\,t Cit,表示把 a [ i ] a[i] a[i]改成 t t t.
1 ≤ n , m ≤ 1 0 4 , 0 ≤ a [ i ] , t ≤ 1 0 9 1\leq n,m\leq 10^4,0\leq a[i],t\leq 10^9 1n,m104,0a[i],t109.

POJ2104的拓展.

由于现在有了真正的修改操作,所以考虑把修改拆成两个,一个是把原来这个位置上的数的贡献撤销,一个是给这个位置上加入新数的贡献.

具体实现可以看代码.

时间复杂度 O ( ( n + m ) log ⁡ a [ i ] log ⁡ n ) O((n+m)\log a[i]\log n) O((n+m)loga[i]logn).

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=10000,INF=1000000000;

char rc(){
  char c=getchar();
  for (;c<'A'||c>'Z';c=getchar());
  return c;
}

int n,m,c[N+9],a[N+9];

void Add(int p,int v){if (!p) return;for (;p<=n;p+=p&-p) c[p]+=v;}
int Query(int p){int res=0;for (;p;p-=p&-p) res+=c[p];return res;}
int Query(int l,int r){return Query(r)-Query(l-1);}

struct question{
  int opt,x,y,z,id;
  question(int Opt=0,int X=0,int Y=0,int Z=0,int Id=0){opt=Opt;x=X;y=Y;z=Z;id=Id;}
}q[N*3+9],lq[N*3+9],rq[N*3+9];
int cq,ans[N+9];

void Divide(question *q,int L,int R,int h,int t){
  if (h>t) return;
  if (L==R){
  	for (int i=h;i<=t;++i)
  	  if (!q[i].opt) ans[q[i].id]=L;
  	return;
  }
  int mid=L+R>>1,lt=0,rt=0,tmp;
  for (int i=h;i<=t;++i)
    if (q[i].opt){
	  if (q[i].y>mid) rq[++rt]=q[i];
	  else Add(q[i].x,q[i].z),lq[++lt]=q[i];
	}else{
	  tmp=Query(q[i].x,q[i].y);
	  if (q[i].z<=tmp) lq[++lt]=q[i];
	  else q[i].z-=tmp,rq[++rt]=q[i];
	}
  for (int i=h;i<=t;++i)
    if (q[i].opt&&q[i].y<=mid) Add(q[i].x,-q[i].z);
  for (int i=1;i<=lt;++i) q[h+i-1]=lq[i];
  for (int i=1;i<=rt;++i) q[h+lt+i-1]=rq[i];
  Divide(q,L,mid,h,h+lt-1);
  Divide(q,mid+1,R,h+lt,t);
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i){
    scanf("%d",&a[i]);
    q[++cq]=question(1,i,a[i],1,0);
  }
  char opt;
  int x,y,z;
  for (int i=1;i<=m;++i){
    opt=rc();
	if (opt=='C'){
	  scanf("%d%d",&x,&y);
	  q[++cq]=question(1,x,a[x],-1,0);
	  q[++cq]=question(1,x,y,1,0);
	  a[x]=y;
	}else{
	  scanf("%d%d%d",&x,&y,&z);
	  q[++cq]=question(0,x,y,z,i);
	}
	ans[i]=-1;
  }
}

Abigail work(){
  Divide(q,0,INF,1,cq);
}

Abigail outo(){
  for (int i=1;i<=m;++i)
    if (ans[i]^-1) printf("%d\n",ans[i]);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值