[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));
}
}
}