1.3闲话
昨天晚上班级内部大换宿舍但是我宿舍没变,只是从左窗上平移到右窗上非常好
推歌:蝶变\洛天依
由二逼平衡树引发了一些树套树的学习
首先看一道题目
这是一道模板题。
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
查询 \(x\) 在区间内的排名
查询区间内排名为 \(k\) 的值
修改某一位置上的数值
查询 \(x\) 在区间内的前驱(前驱定义为小于 \(x\) ,且最大的数)
查询 \(x\) 在区间内的后继(后继定义为大于 \(x\) ,且最小的数)
查询前驱/后继可以用平衡树秒了
修改某一位置上的数值可以平衡树直接del然后再ins即可
查询全局排名为 \(k\) 的值可以用平衡树解决,但是区间肯定不能裸平衡树非常不好
查询区间 \(x\) 在全局的排名可以用平衡树解决,但是区间肯定也不能裸平衡树非常恼
那么有一个显而易见的做法
那就是用一大堆平衡树维护
我们可以维护一个线段树,让线段树的每个节点都是一颗平衡树即可解决非常好
那么我们就解决了这道二逼平衡树模板,注意一些细节,比如最开始我开了\(1e6\)棵平衡树直接似了
点击查看代码
#include<bits/stdc++.h>
#define int long long
namespace IO{
inline void close(){std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);}
inline void Fire(){freopen("data.in","r",stdin);freopen("data.out","w",stdout);}
inline int read(){int s = 0,w = 1;char ch = getchar();while(ch<'0'||ch>'9'){ if(ch == '-') w = -1;ch = getchar();}while(ch>='0'&&ch<='9'){ s = s*10+ch-'0';ch = getchar();}return s*w;}
inline void write(int x){char F[200];int tmp=x>0?x:-x,cnt=0;;if(x<0)putchar('-') ;while(tmp>0){F[cnt++]=tmp%10+'0';tmp/=10;}while(cnt>0)putchar(F[--cnt]);}
}
#define close IO::close
#define Fire IO::Fire
#define read IO::read
#define write IO::write
#define cin std::cin
#define cout std::cout
#define endl '\n'
#define min(a,b) ((a)<(b))?(a):(b)
#define max(a,b) ((a)>(b))?(a):(b)
const int N=1000005;
const int INF=(1ll<<31)-1;
int a[N],mx=-INF,ret;
class Spaly{
private:
int f[N],ch[N][2];
public:
int key[N],lazytag=0,sz,root[N],totf=0,cnt[N],siz[N];
inline void clear(int x){
ch[x][0]=ch[x][1]=f[x]=cnt[x]=key[x]=siz[x]=0;
}
inline int get(int x){
return ch[f[x]][1]==x;
}
inline void update(int x){
if(x){
siz[x]=cnt[x];
if(ch[x][0])
siz[x]+=siz[ch[x][0]];
if(ch[x][1])
siz[x]+=siz[ch[x][1]];
}
}
inline void rotate(int x){
int old=f[x],oldf=f[old],which=get(x);
ch[old][which]=ch[x][which^1];
f[ch[old][which]]=old;
ch[x][which^1]=old;
f[old]=x;f[x]=oldf;
if(oldf)
ch[oldf][ch[oldf][1]==old]=x;
update(old);
update(x);
}
inline void splay(int x){
for(int fa;fa=f[x];rotate(x))
if(f[fa])
rotate(get(x)==get(fa)? fa : x);
}
inline void insert(int idx,int v){
if(root[idx]==0){
++sz;
root[idx]=sz;
ch[root[idx]][0]=ch[root[idx]][1]=f[root[idx]]=0;
key[root[idx]]=v;
cnt[root[idx]]=siz[root[idx]]=1;
return;
}
int cur=root[idx],fa=0;
while(1){
if(key[cur]==v){
++cnt[cur];
update(fa);
splay(cur);
root[idx]=cur;
return;
}
fa=cur;
cur=ch[cur][key[cur]<v];
if(cur==0){
++sz;
ch[sz][0]=ch[sz][1]=0;
key[sz]=v;
siz[sz]=1;
cnt[sz]=1;
f[sz]=fa;
ch[fa][key[fa]<v]=sz;
update(fa);
splay(sz);
root[idx]=sz;
return;
}
}
}
void FIND(int idx,int x) {
int now=root[idx];
while(1){
if(key[now]==x) {
splay(now);
root[idx]=now;
return ;
}
now=ch[now][key[now]<x];
}
}
int findth(int idx,int x) {
int now=root[idx],ans=0;
while(1){
if(!now) return ans;
if(key[now]==x) return ans+(ch[now][0]?siz[ch[now][0]]:0);
if(key[now]<x) ans+=(ch[now][0]?siz[ch[now][0]]:0)+cnt[now];
now=ch[now][key[now]<x];
}
}
inline int pre(int idx){
int cur=ch[root[idx]][0];
while(ch[cur][1])
cur=ch[cur][1];
return cur;
}
inline int nxt(int idx){
int cur=ch[root[idx]][1];
while(ch[cur][0])
cur=ch[cur][0];
return cur;
}
inline void del(int v){
if(cnt[root[v]]>1){
--cnt[root[v]];
update(root[v]);
return;
}
if(!ch[root[v]][0] && !ch[root[v]][1]){
clear(root[v]);
root[v]=0;
return;
}
if(!ch[root[v]][0]){
int oldroot=root[v];
root[v]=ch[oldroot][1];
f[root[v]]=0;
clear(oldroot);
return;
}
else if(!ch[root[v]][1]){
int oldroot=root[v];
root[v]=ch[root[v]][0];
f[root[v]]=0;
clear(oldroot);
return;
}
int lpre=pre(v),oldroot=root[v];
splay(lpre);root[v]=lpre;
f[ch[oldroot][1]]=root[v];
ch[root[v]][1]=ch[oldroot][1];
update(root[v]);
return;
}
inline int PRE(int idx,int x){
int cur=root[idx],ans=-INF;
while(cur){
if(key[cur]<x) {
ans=max(ans,key[cur]);
cur=ch[cur][1];
}
else cur=ch[cur][0];
}
return ans;
}
inline int NXT(int idx,int x){
int cur=root[idx],ans=INF;
while(cur){
if(key[cur]>x){
ans=min(ans,key[cur]);
cur=ch[cur][0];
}
else cur=ch[cur][1];
}
return ans;
}
}TrEE;
class ST{
public:
#define lc q<<1
#define rc q<<1|1
#define mid ((l+r)>>1)
void insert(int q,int l,int r,int x,int v) {
TrEE.insert(q,v);
if(l==r) return;
if(x<=mid) insert(lc,l,mid,x,v);
else insert(rc,mid+1,r,x,v);
return ;
}
int Askrank(int q,int l,int r,int ql,int qr,int x){
if(ql>r || qr<l)
return 0;
if(l>=ql&&r<=qr)
return TrEE.findth(q,x);
// if(mid>=ql) ;
// if(mid<qr) ans+=;
return Askrank(lc,l,mid,ql,qr,x)+Askrank(rc,mid+1,r,ql,qr,x);
}
void change(int q,int l,int r,int pos,int k) {
TrEE.FIND(q,a[pos]);
TrEE.del(q);
TrEE.insert(q,k);
if(l==r) return;
if(pos<=mid) change(lc,l,mid,pos,k);
else change(rc,mid+1,r,pos,k);
return ;
}
int Pre(int q,int l,int r,int ql,int qr,int k) {
if(ql<=l&&r<=qr)
return TrEE.PRE(q,k);
int ans=-INF;
if(mid>=ql) ans=max(ans,Pre(lc,l,mid,ql,qr,k));
if(mid<qr) ans=max(ans,Pre(rc,mid+1,r,ql,qr,k));
return ans;
}
int Nxt(int q,int l,int r,int ql,int qr,int k) {
if(ql<=l&&r<=qr)
return TrEE.NXT(q,k);
int ans=INF;
if(mid>=ql) ans=min(ans,Nxt(lc,l,mid,ql,qr,k));
if(mid<qr) ans=min(ans,Nxt(rc,mid+1,r,ql,qr,k));
return ans;
}
}ST;
signed main(){
// Fire();
int n=read(),m=read(),qwq=m;
for(int i=1;i<=n;i++) {
a[i]=read();mx=max(mx,a[i]);
ST.insert(1,1,n,i,a[i]);
}
while(m--) {
int opt=read(),l,r,pos,k;
if(opt==1) {
l=read(),r=read(),k=read();
int res=ST.Askrank(1,1,n,l,r,k);
printf("%d\n",res+1);
}
if(opt==2) {
l=read(),r=read(),k=read();
int L=0,R=mx,ret;
while(L<=R) {
int MID=(L+R)>>1;
int res=ST.Askrank(1,1,n,l,r,MID);
if(res<k) {
L=MID+1;
ret=MID;
}
else R=MID-1;
}
printf("%d\n",ret);
}
if(opt==3) {
pos=read(),k=read();
ST.change(1,1,n,pos,k);
mx=max(mx,k);
a[pos]=k;
}
if(opt==4) {
l=read(),r=read(),k=read();
int res=ST.Pre(1,1,n,l,r,k);
printf("%d\n",res);
}
if(opt==5) {
l=read(),r=read(),k=read();
int res=ST.Nxt(1,1,n,l,r,k);
printf("%d\n",res);
}
}
return 0;
}
然后我一晚上就写了这一道题剩下的咕咕咕了
非常好,两百行居然打住了
但是下一道题我就一点都看不懂了悲悲悲
要不先练一下基础的树套树和splay再看后面几道题