ZOJ - 2112

\[\
\]

(那些说这道题是树状数组套主席树的人一定对主席树有误解!)

这里我们用树状数组套线段树来解决来写

首先 , 我们需要有n棵线段树(不是\(n^2\)空间,别慌)

我们用这些线段树存储值域$ [l,r] $内数的个数

基于主席树的思想,我们的线段树是要相减的,记录的是前缀

由于要更新前缀,我们必须快速更新,所以采用树状数组来写

事实上,这里线段树的本质并非主席树,而是动态开点的线段树

(这两者是有显著差异的)

这是主席数的单点修改

struct Functional_SegmentTree{
void Add(int p,int pre,int l,int r,int x,int y){
s[p]=s[pre]+y;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) rs[p]=rs[pre],Add(ls[p]=++cnt,ls[pre],l,mid,x,y);
else ls[p]=ls[pre],Add(rs[p]=++cnt,rs[pre],mid+1,r,x,y);
}
};

是路径上的所有点都要新开节点,而实际上动点线段树不是开新点,只是当你的儿子要访问了却还未开出来时才需要开

所以代码应该是这样的

void Add(int p,int l,int r,int x,int y){
s[p]+=y;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) Add(ls[p]?ls[p]:(ls[cnt+1]=rs[cnt+1]=s[cnt+1]=0,ls[p]=++cnt),l,mid,x,y);
else Add(rs[p]?rs[p]:(ls[cnt+1]=rs[cnt+1]=s[cnt+1]=0,rs[p]=++cnt),mid+1,r,x,y);
}

(略有压行)

所以整体上应该是树状数组更新动点线段树

但是由于这题卡空间 (过于罪恶)

所以我们应该先开一个主席树存下原来的值

(为什么这样能省空间呢?因为树状数组更新的空间复杂度是\(log^2(n)\),主席树更新是log(n)的)

于是代码会长这样

const int N=50100,M=10010,K=1520110;
int n,m; int ncnt;
int a[N],b[N+M],c[M],d[M],e[M];
int cnt;
int ls[K],rs[K],s[K];
int rt[N]; struct hjt{
void Add(int p,int pre,int l,int r,int x,int y){
s[p]=s[pre]+y;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) rs[p]=rs[pre],Add(ls[p]=++cnt,ls[pre],l,mid,x,y);
else ls[p]=ls[pre],Add(rs[p]=++cnt,rs[pre],mid+1,r,x,y);
}
}H;
struct sts{
void Add(int p,int l,int r,int x,int y){
s[p]+=y;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) Add(ls[p]?ls[p]:(ls[cnt+1]=rs[cnt+1]=s[cnt+1]=0,ls[p]=++cnt),l,mid,x,y);
else Add(rs[p]?rs[p]:(ls[cnt+1]=rs[cnt+1]=s[cnt+1]=0,rs[p]=++cnt),mid+1,r,x,y);
}
int T[N];
vector <int> X,Y;
int Que(int l,int r,int k){
if(l==r) return l;
int mid=(l+r)>>1;
int t=0;
rep(i,0,X.size()-1) t+=s[ls[X[i]]];
rep(i,0,Y.size()-1) t-=s[ls[Y[i]]];
if(t>=k) {
rep(i,0,X.size()-1) X[i]=ls[X[i]];
rep(i,0,Y.size()-1) Y[i]=ls[Y[i]];
return Que(l,mid,k);
} else {
rep(i,0,X.size()-1) X[i]=rs[X[i]];
rep(i,0,Y.size()-1) Y[i]=rs[Y[i]];
return Que(mid+1,r,k-t);
}
}
int query(int l,int r,int k){
int p=r; X.clear();X.push_back(rt[r]);
while(p) X.push_back(T[p]),p-=p&-p;
p=l-1;Y.clear();Y.push_back(rt[l-1]);
while(p) Y.push_back(T[p]),p-=p&-p;
return Que(1,ncnt,k);
}
void Upd(int p,int x,int y){
while(p<=n) Add(T[p]?T[p]:(ls[cnt+1]=rs[cnt+1]=s[cnt+1]=0,T[p]=++cnt),1,ncnt,x,y),p+=p&-p;
}
}S; char opt[N][1]; int main(){
rep(kase,1,rd()){
n=rd(),m=rd();
cnt=0; memset(S.T,0,sizeof S.T);
rep(i,1,n) a[i]=b[i]=rd();
ncnt=n;
rep(i,1,m) {
scanf("%s",opt[i]);
if(opt[i][0]=='Q'){
c[i]=rd(),d[i]=rd(),e[i]=rd();
} else {
c[i]=rd(),d[i]=rd();
b[++ncnt]=d[i];
}
}
sort(b+1,b+ncnt+1);ncnt=unique(b+1,b+ncnt+1)-b-1;
rep(i,1,n) {
a[i]=lower_bound(b+1,b+ncnt+1,a[i])-b;
H.Add(rt[i]=++cnt,rt[i-1],1,ncnt,a[i],1);
}
rep(i,1,m){
if(opt[i][0]=='Q'){
int ans=S.query(c[i],d[i],e[i]);
printf("%d\n",b[ans]);
}else {
d[i]=lower_bound(b+1,b+ncnt+1,d[i])-b;
S.Upd(c[i],a[c[i]],-1);
S.Upd(c[i],a[c[i]]=d[i],1);
}
}
}
}

最新文章

  1. Linux 下 查看以及修改文件权限
  2. CSS控制样式的三种方式优先级对比验证
  3. 你真的了解UIWindow吗?
  4. springmvc视图解析流程
  5. 使用Visual Studio制作安装包
  6. 单步运行linux kernel ?
  7. ibatis ORA-00911: 无效字符
  8. JS获取当前日期时间并定时刷新
  9. [CSS3] 学习笔记-CSS3常用操作
  10. 7.modifier插件的自定义和使用
  11. RE:考勤系统的复盘
  12. 看图说话,P2P 分享率 90% 以上的 P2P-CDN 服务,来了!
  13. 构建现代Web应用时究竟是选择传统web应用还是SPA
  14. Android四大组件之 --- Service入门
  15. 最简单的方式离线部署Python依赖包
  16. 判断当前的Activity的是否处于栈顶
  17. 第六章 键盘(SYSMETS4)
  18. zookeeper集群崩溃处理
  19. The Best Path HDU - 5883(欧拉回路 &amp;&amp; 欧拉路径)
  20. python之旅:python中range()和len()函数区别

热门文章

  1. 2019 京东java面试笔试总结 (含面试题解析)
  2. pyspider最佳实践
  3. Java虚拟机是怎么new的对象?
  4. Devops K8s
  5. 《微信小程序项目开发实战:用WePY、mpvue、Taro打造高效的小程序》(笔记1)WePY开发环境的安装
  6. QT之Qt之Q_PROPERTY宏理解
  7. vim巧妙用法
  8. SDk编程基础
  9. Django之DRF源码分析(四)---频率认证组件
  10. zabbix--自动注册