[BZOJ3196] 二逼平衡树 [权值线段树套位置平衡树]
题面
思路
没错我就是要不走寻常路!
看看那些外层位置数据结构,必须二分的,$O(n\log^3 n)$的做法吧!
看看那些cdq分治/树状数组套线段树的,空间$O(n\log^2 n)$挤挤挤开不下的做法吧!
这些都不是最优秀的,我来写一种理论复杂度为时间$O(n\log n\log (m+n))$,空间$O(n\log (n+m))$的做法
我们首先考虑时间问题:为什么传统做法的复杂度是3个$log$的?
核心问题是他们有一个二分,否则无法处理第二种询问
那么可以看到第二种询问本质上就是关于区间中权值从小到大排序以后提取出某一位作为答案
考虑把内外层数据结构的意义交换:外层维护权值,内层维护位置
对于询问一:
这里就是简单查询,在外层数对应的区间范围内查询一共有多少个内层数上位于询问区间内的点即可
实现函数为$getk$
对于询问二:
首先,答案就是这个区间内排名为第$k$的数的权值
我们在外层树上二分查找,每次询问内层中外层当前节点的左儿子询问区间中节点个数
如果左边比需要的大就递归进入左边,否则需要的值先减去左边的总数,再进入右边
最后返回叶节点的权值
代码实现为$query$函数
对于修改三
删除再插入即可
对于询问四
我们首先查询,在给定区间内,比询问的数小的数有多少个(用$getk$函数)
然后,其实这个数量,等价于排名
所以我们把排名+1,再用$query$函数输出这个排名对应的数
对于询问五,基本与询问4等价,不赘述
综上,这里的核心思路其实就是:
比某个数小的数字的个数就是这个数的排名
实现的时候,注意外层和内层不同于常规树套树
使用指针or引用可以得到比较好的效果
同时,如果不离散化,时间复杂度和空间复杂度里面的那个$\log(n+m)$都会变成$\log 10^8$
不过也是可以过的
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<queue>
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
namespace spl{
int ch[6000010][2],w[6000010],siz[6000010],fa[6000010],cnt;
void update(int cur){
siz[cur]=siz[ch[cur][0]]+siz[ch[cur][1]]+1;
}
int newnode(int f,int val){
cnt++;
fa[cnt]=f;siz[cnt]=1;ch[cnt][0]=ch[cnt][1]=0;w[cnt]=val;
return cnt;
}
bool get(int cur){return ch[fa[cur]][1]==cur;}
void rotate(int x){
int f=fa[x],ff=fa[f],son=get(x);
ch[f][son]=ch[x][son^1];
if(ch[f][son]) fa[ch[f][son]]=f;
fa[f]=x;ch[x][son^1]=f;
fa[x]=ff;
if(ff) ch[ff][ch[ff][1]==f]=x;
update(f);update(x);
}
void splay(int x,int to,int &root){
for(int f;(f=fa[x])!=to;rotate(x)){
if(fa[f]!=to)
rotate((get(x)==get(f))?f:x);
}
if(!to) root=x;
}
void insert(int &cur,int val,int f,int &root){
if(!cur){
cur=newnode(f,val);
splay(cur,0,root);
return;
}
siz[cur]++;
if(w[cur]>=val) insert(ch[cur][0],val,cur,root);
else insert(ch[cur][1],val,cur,root);
}
int getpos(int cur,int val){
if(w[cur]==val) return cur;
if(w[cur]>val) return getpos(ch[cur][0],val);
else return getpos(ch[cur][1],val);
}
int pre(int root){
int u=ch[root][0];
while(ch[u][1]) u=ch[u][1];
return u;
}
void del(int val,int &root){
int cur=getpos(root,val);splay(cur,0,root);
if(!ch[cur][0]&&!ch[cur][1]){root=0;return;}
if(!ch[cur][0]&&ch[cur][1]){
fa[ch[cur][1]]=0;root=ch[cur][1];
siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0;
return;
}
if(ch[cur][0]&&!ch[cur][1]){
fa[ch[cur][0]]=0;root=ch[cur][0];
siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0;
return;
}
int p=pre(cur),suf=ch[cur][1];
splay(p,0,root);
assert(fa[cur]==p);
assert(ch[cur][0]==0);
ch[p][1]=suf;fa[suf]=p;update(p);
siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0;
}
int pre(int val,int &root){
int u=root,re=0,minn=1e9;
while(u){
if(w[u]>=val) u=ch[u][0];
else{
if(val-w[u]<minn){minn=val-w[u];re=u;}
u=ch[u][1];
}
}
splay(re,0,root);
return re;
}
int suf(int val,int &root){
int u=root,re=0,minn=1e9;
while(u){
if(w[u]<=val) u=ch[u][1];
else{
if(w[u]-val<minn){minn=w[u]-val;re=u;}
u=ch[u][0];
}
}
splay(re,0,root);
return re;
}
int query(int &root,int l,int r){
int x=pre(l,root),y=suf(r,root);
splay(x,0,root);splay(y,x,root);
return siz[ch[y][0]];
}
}
namespace ct{
int cnt,re;queue<int>q;
int newnode(){
if(!q.empty()){re=q.front();q.pop();return re;}
else return ++cnt;
}
void del(int num){
q.push(num);
}
}
namespace seg{
int root[1000010],ch[1000010][2];
void insert(int &cur,int l,int r,int pos,int val){//pos is value in original list, val is position
if(!cur){
cur=ct::newnode();
spl::insert(root[cur],-1e6,0,root[cur]);
spl::insert(root[cur],1e6,0,root[cur]);
}
spl::insert(root[cur],val,0,root[cur]);
if(l==r) return;
int mid=(l+r)>>1;
if(mid>=pos) insert(ch[cur][0],l,mid,pos,val);
else insert(ch[cur][1],mid+1,r,pos,val);
}
void del(int &cur,int l,int r,int pos,int val){
spl::del(val,root[cur]);
if(l^r){
int mid=(l+r)>>1;
if(mid>=pos) del(ch[cur][0],l,mid,pos,val);
else del(ch[cur][1],mid+1,r,pos,val);
}
if(spl::siz[root[cur]]==2){
ct::del(cur);
spl::del(-1e6,root[cur]);
spl::del(1e6,root[cur]);
ch[cur][0]=ch[cur][1]=root[cur]=0;
cur=0;
}
}
int getk(int &cur,int l,int r,int qx,int qy,int ql,int qr){
if(!cur) return 0;
if(l>=ql&&r<=qr) return spl::query(root[cur],qx,qy);
int mid=(l+r)>>1,re=0;
if(mid>=ql) re+=getk(ch[cur][0],l,mid,qx,qy,ql,qr);
if(mid<qr) re+=getk(ch[cur][1],mid+1,r,qx,qy,ql,qr);
return re;
}
int query(int cur,int l,int r,int pos,int qx,int qy){
if(l==r) return l;
int tmp=0;
if(ch[cur][0]) tmp=spl::query(root[ch[cur][0]],qx,qy);
int mid=(l+r)>>1;
if(tmp>=pos) return query(ch[cur][0],l,mid,pos,qx,qy);
else return query(ch[cur][1],mid+1,r,pos-tmp,qx,qy);
}
}
int a[50010],n,m,rt=0;
int main(){
using namespace seg;
n=read();m=read();
int i,t1,t2,t3,t4,tmp;
for(i=1;i<=n;i++){
a[i]=read();
insert(rt,0,1e8,a[i],i);
}
while(m--){
t1=read();
if(t1==1){
t2=read();t3=read();t4=read();
if(t4==0) puts("1");
else printf("%d\n",getk(rt,0,1e8,t2,t3,0,t4-1)+1);
}
if(t1==2){
t2=read();t3=read();t4=read();
printf("%d\n",query(rt,0,1e8,t4,t2,t3));
}
if(t1==3){
t2=read();t3=read();
del(rt,0,1e8,a[t2],t2);
insert(rt,0,1e8,a[t2]=t3,t2);
}
if(t1==4){
t2=read();t3=read();t4=read();
tmp=getk(rt,0,1e8,t2,t3,0,t4-1);
if(tmp==0) puts("-2147483647");
else printf("%d\n",query(rt,0,1e8,tmp,t2,t3));
}
if(t1==5){
t2=read();t3=read();t4=read();
tmp=getk(rt,0,1e8,t2,t3,0,t4);
if(tmp==t3-t2+1) puts("2147483647");
else printf("%d\n",query(rt,0,1e8,tmp+1,t2,t3));
}
}
}
[BZOJ3196] 二逼平衡树 [权值线段树套位置平衡树]的更多相关文章
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- 【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree
题目描述 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了.蒟蒻Bob ...
- bzoj4605: 崂山白花蛇草水 权值线段树套KDtree
bzoj4605: 崂山白花蛇草水 链接 bzoj loj 思路 强制在线,那就权值线段树套KDtree好了,没啥好讲的. KDtree要加平衡因子来重构.另外,那水真难喝. 错误 树套树一边写过了, ...
- [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)
[BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...
- cogs 1829. [Tyvj 1728]普通平衡树 权值线段树
1829. [Tyvj 1728]普通平衡树 ★★★ 输入文件:phs.in 输出文件:phs.out 简单对比时间限制:1 s 内存限制:1000 MB [题目描述] 您需要写一种数 ...
- [BZOJ3600] 没有人的算术 [重量平衡树+权值线段树]
题面 传送门 思路 这道题目是陈立杰论文<重量平衡树和后缀平衡树在信息学奥赛中的应用 >中关于重量平衡树维护序列排名算法的一个应用 具体方法为:令根节点保存一个实数区间$[0,1]$ 若当 ...
- BZOJ3110[Zjoi2013]K大数查询——权值线段树套线段树
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是 ...
- 崂山白花蛇草水 权值线段树套KDtree
Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实 力,他轻松地进了山东省省队,现在便是他履行诺言的时 ...
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...
随机推荐
- 修改第三方库内容,carsh提示"image not found"
在图示位置把提示的东西加上即可 参考: iOS app with framework crashed on device, dyld: Library not loaded, Xcode 6 Beta ...
- BZOJ1004: [HNOI2008]Cards(Burnside引理 背包dp)
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4255 Solved: 2582[Submit][Status][Discuss] Descript ...
- java后台输入数据的2种方式
java后台输入数据的2种方式 (1) import java.io.BufferedReader; import java.io.InputStreamReader; public class 输入 ...
- datatables 给字段设置默认值,屏蔽没有字段的错误
我们返回的数据不能保证都是正常的,可能包含 null ,显然这个对于最终用户来说是不友好的,那么我们可以这么处理 先有如下数据格式: //示例数据 { data:[ {"id":1 ...
- 【Python 2 到 3 系列】 print 是函数
v3.0 以前,print一直作为语法结构存在,他是python语法的一部分:这个理解起来可能有点蹩脚,但的确是这样. print 一直被定以为一个statement,也就是说,他跟return/tr ...
- MLT教程:从BXL文件导入Altium Designer原理图封装和PCB封装
在TI官网的封装文件中提供弄BXL文件可以导出Altium Designer的封装库和原理图库. 这个界面往下面拉会看到: 然后可以下载各种封装的bxl文件了.下面视频说明bxl文件如何导出成功. 如 ...
- Java 的单元测试
有点需要注意,当 JUnit 主线程退出,子线程也会跟着退出,需要使用子线程的 join() 方法使主线程等待 Maven 依赖 <dependency> <groupId>j ...
- 2 js的20/80关键知识
1. 2 var a = 1; undefined a 1 alert(a); undefined var b = true; var c = "Hi"; undefined al ...
- webstrom Certificate validation failed
今天好烦.因为装了一个webstrome,我的svn不管用了. 为了防止webstrom,我的日期改了,日期改了,csdn登不上去了.告诉我时期不对. 草草哦哦. 这就是那个svn出问题之后的画面. ...
- APP遇到大量的真实手机号刷注册用户该如何应对?
欢迎访问网易云社区,了解更多网易技术产品运营经验. 在说如何应对之前,先给各位梳理移动端APP可能遇到哪些作弊风险.1. 渠道商刷量,伪造大量的下载量和装机量,但没有新用户注册:2. 对于电商.P2P ...