zoj 2112 单点修改的主席树(树状数组套主席树)
题目大意:
区间第k大问题+单点修改
基本思路:
这个题有用整体二分,cdq分治,还有主席树+平衡树的,还有就是主席树+树状数组。
我采用的是b站电子科大大佬的主席树写法,尤其喜欢他的离散化方法,所以就这么写了。
下面对代码进行下解释(当然,详细解释看注解):
root[i]:第i棵树根节点的编号,i就是原来序列的下标,代码里从1开始
tr[i]:树状数组第i棵树根节点的编号
ur[i],ul[i]:临时用来存从第i棵树根节点编号到左右子树编号一直到底。
-----------------------------------------------------------------------------
详细注解,转自:https://blog.csdn.net/WilliamSun0122/article/details/77885781
其实静态主席树我们弄清楚之后,动态的可以很快学会。因为动态主席树就是在静态主席树的基础上增加了一批用树状数组思维维护的线段树。
我们以下面的例子讲解。
5 3
3 2 1 4 7
Q 1 4 3 询问区间[1,4]第3小数
C 2 6 把第2个数变为6
Q 2 5 3 询问区间[2,5]第3小数
n是原序列个数
T[i]表示第i棵线段树的根节点编号
S[i]表示树状数组思维建的第i棵线段树的根节点编号
L[i]表示节点i的左子节点编号
R[i]表示节点i的右子节点编号
sum[i]表示节点i对应区间中数的个数。
这里离散化建树过程和静态主席树有一点不同,我们必须把所有询问先存起来并且把改变的数也加入到原序列中再离散化建树,会导致空间复杂度和静态有所区别(之前讲静态的时候提过)。所以这里我们离散化后序列为3 2 1 4 6 5分别对应原序列的3 2 1 4 7和改变后的6。
之后同静态一样建空树,按原序列前缀建树,相信不用我说了。(画图有点麻烦,我借鉴参考博客的图,这个图是没错的)
接下来就是重点了,对于题目给出的修改操作,我们新建一批线段树来记录更新,这些线段树以树状数组的思维来维护。
一开始,S[0]、S[1]、S[2]、S[3]、S[4]、S[5] (注意一共有n+1个 即 0到n)(树状数组的每个节点)这些都与T[0]相同(也就是每个节点建了一棵空树)。
对于C 2 6 这个操作, 我们只需要减去一个2,加上一个5(对应改变后的6)即可。
这个更新我们按树状数组的思想更新,比如这里的减2,我们要从i=2(原序列中第2个数2在离散化后序列中的位置)即S[2]开始更新,并往上lowbit(i)直到大于5,这里我们会更新S[2]和S[4]。
边看图边理解(这个图最后应该是在节点5那里减1)
对于加5同样是从S[2]开始更新
(这个图最后应该是在节点10那里加1)
这样我们查询的时候T[]和静态一样,再按树状数组的思维加上S[]就可算出每个节点对应区间中数的个数,再按静态的思想查询即可。
对于原序列n个数,m次询问
空间复杂度(我的理解)
因为这里我们要加入询问中数离散化后再建树,所以建树4*(n+m)
按原序列更新T[],nlog2(n+m)log2(n+m)
每次询问更新S[],2log2nlog2n(按树状数组最多更新的节点)log2(n+m)log2(n+m)(每次最多更新线段树中的节点)
加起来即可,不过你真这么开数组的话肯定会MLE。
以zoj2112为例,参考博客开的范围是2*(n+m)log2(n+m)log2(n+m)(大概,我是强行解释,我范围开大会Segmentation fault,开小会RE)
我猜大概是因为如果每次询问都是修改操作的话按静态中的开应该会是(n+m)log2(n+m)log2(n+m)还要加上一个S[],所以要*2。
时间复杂度(我的理解)
mlog2nlog2nlog2(n+m)
代码如下:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f;
const int maxn = 60000+10;
int n,q,num,cnt;
int root[maxn],tr[maxn],arr[maxn],ul[maxn],ur[maxn];
vector<int>vec;
struct Node{
int l,r,sum;
}T[maxn*32];
struct Ques{
bool flag;
int l,r,k;
}ques[maxn];
int getid(int x){
return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
}
void update(int l,int r,int& x,int y,int pos,int val){
T[++cnt]=T[y];
T[cnt].sum+=val;
x=cnt;
if(l==r){
return;
}
int mid=(l+r)/2;
if(pos<=mid){
update(l,mid,T[x].l,T[y].l,pos,val);
}else{
update(mid+1,r,T[x].r,T[y].r,pos,val);
}
}
int lowbit(int x){
return x&(-x);
}
void add(int x,int val){
int ans=getid(arr[x]);
while(x<=n){
update(1,num,tr[x],tr[x],ans,val);
x+=lowbit(x);
}
}
int getSum(int x,bool flag){
int ans=0;
while(x>0){
if(flag){
ans+=T[T[ur[x]].l].sum;
}else{
ans+=T[T[ul[x]].l].sum;
}
x-=lowbit(x);
}
return ans;
}
int query(int l,int r,int s,int e,int ts,int te,int k){
if(l==r){
return l;
}
int mid=(l+r)/2;
int ans=getSum(e,true)-getSum(s,false)+T[T[te].l].sum-T[T[ts].l].sum;
if(k<=ans){
//从根节点每次将相关所有节点下降一层
for(int i=e;i;i-=lowbit(i)){
ur[i]=T[ur[i]].l;
}
for(int i=s;i;i-=lowbit(i)){
ul[i]=T[ul[i]].l;
}
return query(l,mid,s,e,T[ts].l,T[te].l,k);
}else{
for(int i=e;i;i-=lowbit(i)){
ur[i]=T[ur[i]].r;
}
for(int i=s;i;i-=lowbit(i)){
ul[i]=T[ul[i]].r;
}
return query(mid+1,r,s,e,T[ts].r,T[te].r,k-ans);
}
}
int main(){
int cas;
scanf("%d",&cas);
while(cas--){
cnt=0;
vec.clear();
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
vec.push_back(arr[i]);
}
char op[5];
for(int i=1;i<=q;i++){
scanf("%s",op);
if(op[0]=='Q'){
ques[i].flag=false;
scanf("%d%d%d",&ques[i].l,&ques[i].r,&ques[i].k);
}else{
ques[i].flag=true;
scanf("%d%d",&ques[i].l,&ques[i].r);
vec.push_back(ques[i].r);
}
}
//离散化
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
num=vec.size();
for(int i=1;i<=n;i++){
update(1,num,root[i],root[i-1],getid(arr[i]),1);
}
for(int i=1;i<=n;i++){
tr[i]=root[0];
}
for(int i=1;i<=q;i++){
if(ques[i].flag){
add(ques[i].l,-1);
arr[ques[i].l]=ques[i].r;
add(ques[i].l,1);
}else{
for(int j=ques[i].r;j;j-=lowbit(j)){
ur[j]=tr[j];
}
for(int j=ques[i].l-1;j;j-=lowbit(j)){
ul[j]=tr[j];
}
printf("%d\n",vec[query(1,num,ques[i].l-1,ques[i].r,root[ques[i].l-1],root[ques[i].r],ques[i].k)-1]);
}
}
}
}
zoj 2112 单点修改的主席树(树状数组套主席树)的更多相关文章
- ZOJ 2112 Dynamic Rankings(树状数组套主席树 可修改区间第k小)题解
题意:求区间第k小,节点可修改 思路:如果直接用静态第k小去做,显然我更改一个节点后,后面的树都要改,这个复杂度太高.那么我们想到树状数组思路,树状数组是求前缀和,那么我们可以用树状数组套主席树,求出 ...
- BZOJ2120:数颜色(数状数组套主席树)(带修改的莫对)
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2. R P ...
- BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树
[题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> ...
- P2617 Dynamic Rankings(树状数组套主席树)
P2617 Dynamic Rankings 单点修改,区间查询第k大 当然是无脑树套树了~ 树状数组套主席树就好辣 #include<iostream> #include<cstd ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- bzoj1901--树状数组套主席树
树状数组套主席树模板题... 题目大意: 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[ ...
- BZOJ 1901 Zju2112 Dynamic Rankings ——树状数组套主席树
[题目分析] BZOJ这个题目抄的挺霸气. 主席树是第一时间想到的,但是修改又很麻烦. 看了别人的题解,原来还是可以用均摊的思想,用树状数组套主席树. 学到了新的姿势,2333o(* ̄▽ ̄*)ブ [代 ...
- BZOJ1901 - Dynamic Rankings(树状数组套主席树)
题目大意 给定一个有N个数字的序列,然后又m个指令,指令种类只有两种,形式如下: Q l r k 要求你查询区间[l,r]第k小的数是哪个 C i t 要求你把第i个数修改为t 题解 动态的区间第k ...
- BZOJ_3196_Tyvj 1730 二逼平衡树_树状数组套主席树
BZOJ_3196_Tyvj 1730 二逼平衡树_树状数组套主席树 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排 ...
- [COGS257]动态排名系统 树状数组套主席树
257. 动态排名系统 时间限制:5 s 内存限制:512 MB [问题描述]给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:1.查询A[ ...
随机推荐
- nyoj 471:好多的树(容斥原理)
题目链接: http://acm.nyist.net/JudgeOnline/problem.php?pid=471 还是直接上代码.. #include<bits/stdc++.h> u ...
- threeJS射线拾取机制及案例
前言 在浏览器中浏览三维图形的时候,有时想要与三维图形之间做一些点击事件和交互操作,其中比较常用的一个解决方案就是使用Raycaster对象来实现(射线拾取). 基础知识 世界坐标系:webGL中,世 ...
- 接口参数校验之@Valid与BindingResult
接口方法往往需要对入参做一些校验,从而判断入参是否合格,而javax.validation包为我们提供了一些常用的参数校验注解,使用起来很方便. 下面这个示例是检验入参对象中的password是否为空 ...
- Redis集群的搭建【转】
redis集群的特点: 1.机器多,能够保证redis服务器出现问题后,影响较小 2.自备主从结构,自动的根据算法划分主从结构.动态的实现 3.能够根据主从结构自动的实现高可用 4.实现数据文件的备份 ...
- JDK1.8 动态代理机制及源码解析
动态代理 a) jdk 动态代理 Proxy, 核心思想:通过实现被代理类的所有接口,生成一个字节码文件后构造一个代理对象,通过持有反射构造被代理类的一个实例,再通过invoke反射调用被代理类实例的 ...
- linux设备驱动第三篇:写一个简单的字符设备驱动
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...
- linux设备驱动第二篇:构造和运行模块
上一篇介绍了Linux驱动的概念,以及linux下设备驱动的基本分类情况及其各个分类的依据和差异,这一篇我们来描述如何写一个类似hello world的简单测试驱动程序.而这个驱动的唯一功能就是输 ...
- JVM调优(二)——基于JVisualVM的可视化监控
JVM调优(二)--基于JVisualVM的可视化监控 工具路径://java/jdk1.8xxx/bin/JVisuaVM.exe 监控本地的Tomcat 监控远程Tomcat 监控普通的JAVA进 ...
- 103、Linux 编译 Kaldi 语音识别工具
由于这个开源的语音识别工具Kaldi只能在Linux下面成功编译, 所以这一小节来写如何成功地在Linux下面编译Kaldi工具 (1)第一步,去github 上面把 Kaldi下载下来 git cl ...
- css缓存问题
频繁更换样式,会导致样式缓存, 在实际项目开发过过程中,页面是上传到服务器上的.而为了减少服务器的压力,让用户少加载,浏览器会将图片.css.js缓存到本地中,以便下次访问网站时使用.这样做不仅减少了 ...