给定数组,单点修改,区间查询k小。$(n leq 10000)$
暴力题?挺暴力的。树状数组+动态开点线段树。
树状数组维护每个权值的前缀和。可以视为是树状数组的每个节点开一个主席树吧。
修改:修改树状数组上包含修改点的权值线段树。$O(log^2n)$
查询:像主席树一样不断通过二分缩小区间,区间和变成用树状数组求。由于每次二分要查询的节点是一样的,需要维护这些节点的pointer跳到哪里。$O(log^2n)$话说我没维护这个是$O(log^3n)$的。。。Sinogi大佬写了!%%%比我快(长)很多!
但是这种题n都10000了随便怎么做了吧,,,暴力貌似可过,,,尬
#include<bits/stdc++.h> using namespace std; const int N=20010; inline int read(){ int r=0,c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)) r=r*10+c-'0',c=getchar(); return r; } struct Node{ int ls,rs,sum; }T[N*100]; struct ask{ int opt,l,r,k; }q[N]; int rt[N],n,m,cnt; int a[N],b[N],tot,v; void upd(int &k,int l,int r,int val){ if(!k)k=++cnt; T[k].sum+=v; if(l==r){ return; } int mid=l+r>>1; if(val<=mid)upd(T[k].ls,l,mid,val); else upd(T[k].rs,mid+1,r,val); } void modify(int x,int w){ w=lower_bound(b+1,b+tot+1,w)-b; for(int i=x;i<=n;i+=i&-i) upd(rt[i],1,tot,w); } int pt[N],L,R; int sum(int x,int l,int r){ if(!x)return 0; if(L<=l&&r<=R){ return T[x].sum; } int mid=l+r>>1,ans=0; if(L<=mid)ans+=sum(T[x].ls,l,mid); if(R>mid) ans+=sum(T[x].rs,mid+1,r); return ans; } int query(int l,int r){ int ret=0; for(int i=r;i;i-=i&-i) ret+=sum(rt[i],1,tot); for(int i=l;i;i-=i&-i) ret-=sum(rt[i],1,tot); return ret; } void init(){ n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(),b[++tot]=a[i]; char s[5]; for(int i=1;i<=m;i++){ scanf("%s",s); if(s[0]=='C'){ int x=read(),w=read(); q[i]=(ask){0,x,x,w}; b[++tot]=w; } else{ int l=read(),r=read(),w=read(); q[i]=(ask){1,l,r,w}; b[++tot]=w; } } sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-b-1; } void solve(){ for(int i=1;i<=n;i++) v=1,modify(i,a[i]); for(int i=1;i<=m;i++){ if(!q[i].opt){ int x=q[i].l; v=-1;modify(x,a[x]); v=1;modify(x,a[x]=q[i].k); } else{ int x=q[i].l-1,y=q[i].r,rk=q[i].k; int l=1,r=tot; while(l^r){ int mid=l+r>>1; L=l,R=mid; int t=query(x,y); if(rk<=t)r=mid; else l=mid+1,rk-=t; } printf("%d ",b[l]); } } } int main(){ init(); solve(); }