题目链接
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1112
题目大意
带修改操作的区间第K小问题。
注意:bzoj上的数据范围 n≤10000 ,zju上的数据范围 n≤50000 。
题解
裸的树状数组套主席树。查询的时候二分当前答案区间
[l,r]
,如果在
[l,mid]
区间内的权值个数
sum≥k
,那么转移到
[l,mid]
,否则转移到
[mid+1,r]
并把
k
减去
时间复杂度
O(nlognlogs)
,其中
s
是权值范围,如果写了离散化那么就是
空间复杂度也是
O(nlog2n)
,貌似在zju上卡不过去。
#include <algorithm>
#include <cstdio>
const int N=10005,inf=1000000000;
int tr[N*900],ls[N*900],rs[N*900],a[N],rt[N],n,m,tot=0;
void add(int &now,int l,int r,int x,int y){
if (!now) now=++tot;
tr[now]+=y;
if (l==r) return;
int mid=(l+r)>>1;
if (x<=mid) add(ls[now],l,mid,x,y);
else add(rs[now],mid+1,r,x,y);
}
void insert(int x,int y,int z){
for (;x<=n;x+=x&-x) add(rt[x],0,inf,y,z);
}
int query(int x,int y,int z){
int a[20],b[20],ta=0,tb=0;
for (--x;x;x-=x&-x) a[++ta]=rt[x];
for (;y;y-=y&-y) b[++tb]=rt[y];
int l=0,r=inf;
for (;l<r;){
int mid=(l+r)>>1,sum=0;
for (int i=1;i<=ta;i++) sum-=tr[ls[a[i]]];
for (int i=1;i<=tb;i++) sum+=tr[ls[b[i]]];
if (z<=sum){
r=mid;
for (int i=1;i<=ta;i++) a[i]=ls[a[i]];
for (int i=1;i<=tb;i++) b[i]=ls[b[i]];
}
else{
z-=sum;l=mid+1;
for (int i=1;i<=ta;i++) a[i]=rs[a[i]];
for (int i=1;i<=tb;i++) b[i]=rs[b[i]];
}
}
return l;
}
int main(){
scanf("%d%d\n",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(i,a[i],1);
}
scanf("\n");
for (int i=1;i<=m;i++){
char c;int x,y,z;
scanf("%c%d%d",&c,&x,&y);
if (c=='Q'){
scanf("%d",&z);
int ans=query(x,y,z);
printf("%d\n",ans);
}
else{
insert(x,a[x],-1);
insert(x,y,1);
a[x]=y;
}
scanf("\n");
}
}
树状数组套平衡树,也是经典做法。查询的时候二分答案然后统计,因此时间复杂度 O(nlog3n) ,空间复杂度 O(nlogn) 。
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int N=50005,inf=1000000000;
vector<int> vec;
int ch[N*15][2],v[N*15],cnt[N*15],f[N*15],a[N],rt[N],n,m,tot,cur;
int newnode(){
if (vec.size()){
int x=vec.back();
vec.pop_back();
return x;
}
else return ++tot;
}
bool noroot(int x){return ch[f[x]][0]==x || ch[f[x]][1]==x;}
void pushup(int x){cnt[x]=cnt[ch[x][0]]+cnt[ch[x][1]]+1;}
void rotate(int x){
int y=f[x],tp=ch[y][1]==x;
if (noroot(y)) ch[f[y]][ch[f[y]][1]==y]=x;
f[x]=f[y];
f[ch[y][tp]=ch[x][tp^1]]=y;
f[ch[x][tp^1]=y]=x;
pushup(y);
}
void splay(int id,int x){
if (!x) return;
for (;noroot(x);rotate(x))
if (noroot(f[x])){
if ((ch[f[x]][0]==x)^(ch[f[f[x]]][0]==f[x])) rotate(x);
else rotate(f[x]);
}
pushup(x);
rt[id]=x;
}
void add(int id,int &now,int fa,int x){
if (!now){
v[now=newnode()]=x;f[now]=fa;cnt[now]=1;
splay(id,now);
}
else add(id,ch[now][x>v[now]],now,x);
}
void insert(int x,int y){
for (;x<=n;x+=x&-x) add(x,rt[x],0,y);
}
void del(int id,int now,int x){
if (v[now]==x){
splay(id,now);
if (!ch[now][0]) f[rt[id]=ch[now][1]]=0;
else if (!ch[now][1]) f[rt[id]=ch[now][0]]=0;
else{
int x=ch[now][0];f[x]=0;
for (;ch[x][1];x=ch[x][1]);
f[ch[x][1]=ch[now][1]]=x;
splay(id,ch[now][1]);
}
vec.push_back(now);
ch[now][0]=ch[now][1]=f[now]=cnt[now]=v[now]=0;
}
else del(id,ch[now][x>v[now]],x);
}
void erase(int x,int y){
for (;x<=n;x+=x&-x) del(x,rt[x],y);
}
int get_sum(int id,int now,int x){
if (!now) return 0;
if (v[now]<=x){
int y=get_sum(id,ch[now][1],x);
if (!y) cur=now;
return y+cnt[ch[now][0]]+1;
}
else return get_sum(id,ch[now][0],x);
}
int query(int x,int y){
int sum=0;
for (;x;x-=x&-x){
cur=0;
sum+=get_sum(x,rt[x],y);
splay(x,cur);
}
return sum;
}
int get_ans(int x,int y,int z){
int l=0,r=inf;
for (;l<r;){
int mid=(l+r)>>1;
if (query(y,mid)-query(x-1,mid)>=z) r=mid;
else l=mid+1;
}
return l;
}
int main(){
int D;
for (scanf("%d\n",&D);D--;){
memset(ch,0,sizeof ch);
memset(f,0,sizeof f);
memset(cnt,0,sizeof cnt);
memset(rt,0,sizeof rt);
vec.clear();tot=0;
scanf("%d%d\n",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(i,a[i]);
}
scanf("\n");
for (int i=1;i<=m;i++){
char c;int x,y,z;
scanf("%c%d%d",&c,&x,&y);
if (c=='Q'){
scanf("%d",&z);
int ans=get_ans(x,y,z);
printf("%d\n",ans);
}
else{
erase(x,a[x]);
insert(x,y);
a[x]=y;
}
scanf("\n");
}
}
}
整体二分,这是一种时间空间复杂度都十分优秀的做法,缺点是不支持在线操作。
把读入的
n
个数看成插入操作,修改操作看成删除一个数再插入一个数。二分的时候依然是二分答案,按顺序进行操作,对于那些
时间复杂度
O(nlognlogs)
或
O(nlog2n)
,空间复杂度
O(n)
。
常数也很小,实际运行时间简直快的飞起。
#include <algorithm>
#include <cstdio>
const int inf=1000000000;
int a[50005],f[50005],ans[10005],n,m,tot,cnt;
struct P{
int id,x,y,z;
}q[70005],q1[70005],q2[70005];
void init(){
scanf("%d%d\n",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
q[i]=(P){0,i,a[i],1};
}
scanf("\n");
tot=n;cnt=0;
for (int i=1;i<=m;i++){
char ch;int x,y,z;
scanf("%c%d%d",&ch,&x,&y);
if (ch=='Q'){
scanf("%d",&z);
q[++tot]=(P){++cnt,x-1,y,z};
}
else{
q[++tot]=(P){0,x,a[x],-1};
q[++tot]=(P){0,x,y,1};
a[x]=y;
}
scanf("\n");
}
}
void add(int x,int y){
for (;x<=n;x+=x&-x) f[x]+=y;
}
int query(int x){
int sum=0;
for (;x;x-=x&-x) sum+=f[x];
return sum;
}
void work(int l,int r,int vl,int vr){
if (l>r) return;
if (vl==vr){
for (int i=l;i<=r;i++)
if (q[i].id) ans[q[i].id]=vl;
return;
}
int mid=(vl+vr)>>1,t1=0,t2=0;
for (int i=l;i<=r;i++)
if (q[i].id){
int sum=query(q[i].y)-query(q[i].x);
if (q[i].z<=sum) q1[++t1]=q[i];
else q[i].z-=sum,q2[++t2]=q[i];
}
else if (q[i].y<=mid) add(q[i].x,q[i].z),q1[++t1]=q[i];
else q2[++t2]=q[i];
for (int i=l;i<=r;i++)
if (!q[i].id && q[i].y<=mid) add(q[i].x,-q[i].z);
for (int i=1;i<=t1;i++) q[l+i-1]=q1[i];
for (int i=1;i<=t2;i++) q[l+t1+i-1]=q2[i];
work(l,l+t1-1,vl,mid);
work(l+t1,r,mid+1,vr);
}
int main(){
int D;
for (scanf("%d\n",&D);D--;){
init();
work(1,tot,0,inf);
for (int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
}
}