传送门
线段树套splay,简单地说就是线段树的每一个节点都吊着一颗splay,表示的是线段树当前节点所表示的区间的点,按权值排序。
Q1:线段树常规查询区间,每一次统计小于k的点的个数再相加。
Q2:这个是最麻烦也是最精妙的一问,解决方法是二分答案,每二分到一个答案查询一下这个答案在这个区间内的排名,如果排名等于k+1的话返回它的pre即可。注意这里二分满足条件之后不用查询pre,答案直接为head-1,可以证明head-1一定在序列中。
Q3:相当于线段树的点修改,在splay中删除再插入即可。
Q4:线段树常规查询区间,每一次找区间内比k小的最大的数,然后取max
Q5:类似于Q4,每一次找区间内比k大的最小的数,然后取min
树套树写起来还是比较好写的,但是刚开始姿势不对,尤其是Q2Q4Q5都有比较快的姿势。po主的代码在BZOJ上评测过了,但是tyvjT2组只好弃疗T_T
具体看代码吧,以SegTree为前缀的都是线段树的操作,以Splay为前缀的都是splay的操作,其余的都用比较简单易懂的英文表示w
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=4e6+5;
const int INF=1e9;
int n,m,opt,l,r,k,pos,sz,ans,maxn;
int seq[max_n],f[max_n],ch[max_n][2],size[max_n],cnt[max_n],key[max_n],root[max_n];
inline int in(){
int x=0,f=1; char ch=getchar();
while (ch>'9'||ch<'0'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline void Splay_clear(int x){
f[x]=ch[x][0]=ch[x][1]=size[x]=cnt[x]=key[x]=0;
}
inline int Splay_get(int x){
return ch[f[x]][1]==x;
}
inline void Splay_update(int x){
if (x){
size[x]=cnt[x];
if (ch[x][0]) size[x]+=size[ch[x][0]];
if (ch[x][1]) size[x]+=size[ch[x][1]];
}
}
inline void Splay_rotate(int x){
int old=f[x],oldf=f[old],which=Splay_get(x);
ch[old][which]=ch[x][which^1];
f[ch[old][which]]=old;
ch[x][which^1]=old;
f[old]=x;
if (oldf) ch[oldf][ch[oldf][1]==old]=x;
f[x]=oldf;
Splay_update(old);
Splay_update(x);
}
inline void Splay_splay(int x){
for (int fa;fa=f[x];Splay_rotate(x))
if (f[fa])
Splay_rotate((Splay_get(x)==Splay_get(fa))?fa:x);
}
inline void Splay_insert(int i,int x){
int now=root[i],fa=0;
if (!root[i]){
root[i]=++sz;
f[sz]=ch[sz][0]=ch[sz][1]=0;
size[sz]=cnt[sz]=1; key[sz]=x;
return;
}
while (1){
if (x==key[now]){
cnt[now]++;
Splay_update(fa);
Splay_splay(now);
root[i]=now;
return;
}
fa=now;
now=ch[now][key[now]<x];
if (!now){
++sz;
f[sz]=fa; ch[sz][0]=ch[sz][1]=0;
size[sz]=cnt[sz]=1; key[sz]=x;
ch[fa][key[fa]<x]=sz;
Splay_update(fa);
Splay_splay(sz);
root[i]=sz;
return;
}
}
}
inline void Splay_find(int i,int x){
int now=root[i];
while (1){
if (key[now]==x){
Splay_splay(now);
root[i]=now;
return;
}
else if (key[now]>x) now=ch[now][0];
else if (key[now]<x) now=ch[now][1];
}
}
inline int Splay_pre(int i){
int now=ch[root[i]][0];
while (ch[now][1]) now=ch[now][1];
return now;
}
inline int Splay_next(int i){
int now=ch[root[i]][1];
while (ch[now][0]) now=ch[now][0];
return now;
}
inline void Splay_del(int i){
int now=root[i];
if (cnt[now]>1){
cnt[now]--;
Splay_update(now);
return;
}
if (!ch[now][0]&&!ch[now][1]){
Splay_clear(root[i]);
root[i]=0;
return;
}
if (!ch[now][0]){
int oldroot=now;
root[i]=ch[oldroot][1];
f[root[i]]=0;
Splay_clear(oldroot);
return;
}
if (!ch[now][1]){
int oldroot=now;
root[i]=ch[oldroot][0];
f[root[i]]=0;
Splay_clear(oldroot);
return;
}
int leftbig=Splay_pre(i),oldroot=root[i];
Splay_splay(leftbig); root[i]=leftbig;
ch[root[i]][1]=ch[oldroot][1];
f[ch[oldroot][1]]=root[i];
Splay_clear(oldroot);
Splay_update(root[i]);
return;
}
inline int Splay_findrank(int i,int x){
int now=root[i],ans=0;
while (1){
if (!now) return ans;
if (key[now]==x) return ((ch[now][0])?size[ch[now][0]]:0)+ans;
else if (key[now]<x){
ans+=((ch[now][0])?size[ch[now][0]]:0)+cnt[now];
now=ch[now][1];
}
else if (key[now]>x) now=ch[now][0];
}
}
inline int Splay_findpre(int i,int k){
int now=root[i];
while (now){
if (key[now]<k){
if (ans<key[now])ans=key[now];
now=ch[now][1];
}
else now=ch[now][0];
}
return ans;
}
inline int Splay_findnext(int i,int k){
int now=root[i];
while (now){
if (key[now]>k){
if (ans>key[now]) ans=key[now];
now=ch[now][0];
}
else now=ch[now][1];
}
return ans;
}
inline void SegTree_insert(int now,int l,int r,int x,int v){
int mid=(l+r)>>1;
Splay_insert(now,v);
if (l==r) return;
if (x<=mid) SegTree_insert(now<<1,l,mid,x,v);
else SegTree_insert(now<<1|1,mid+1,r,x,v);
}
inline void SegTree_askrank(int now,int l,int r,int lrange,int rrange,int k){
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange){
ans+=Splay_findrank(now,k);
return;
}
if (lrange<=mid) SegTree_askrank(now<<1,l,mid,lrange,rrange,k);
if (mid+1<=rrange) SegTree_askrank(now<<1|1,mid+1,r,lrange,rrange,k);
}
inline void SegTree_change(int now,int l,int r,int pos,int k){
int mid=(l+r)>>1;
Splay_find(now,seq[pos]); Splay_del(now); Splay_insert(now,k);
if (l==r) return;
if (pos<=mid) SegTree_change(now<<1,l,mid,pos,k);
else SegTree_change(now<<1|1,mid+1,r,pos,k);
}
inline void SegTree_askpre(int now,int l,int r,int lrange,int rrange,int k){
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange){
ans=max(ans,Splay_findpre(now,k));
return;
}
if (lrange<=mid) SegTree_askpre(now<<1,l,mid,lrange,rrange,k);
if (mid+1<=rrange) SegTree_askpre(now<<1|1,mid+1,r,lrange,rrange,k);
}
inline void SegTree_asknext(int now,int l,int r,int lrange,int rrange,int k){
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange){
ans=min(ans,Splay_findnext(now,k));
return;
}
if (lrange<=mid) SegTree_asknext(now<<1,l,mid,lrange,rrange,k);
if (mid+1<=rrange) SegTree_asknext(now<<1|1,mid+1,r,lrange,rrange,k);
}
int main(){
n=in(); m=in();
for (int i=1;i<=n;++i) seq[i]=in(),maxn=max(maxn,seq[i]),SegTree_insert(1,1,n,i,seq[i]);
for (int i=1;i<=m;++i){
opt=in();
switch(opt){
case 1:{
l=in(); r=in(); k=in();
ans=0;
SegTree_askrank(1,1,n,l,r,k);
printf("%d\n",ans+1);
break;
}
case 2:{
l=in(); r=in(); k=in();
int head=0,tail=maxn+1,mid;
while (head!=tail){
int mid=(head+tail)>>1;
ans=0;
SegTree_askrank(1,1,n,l,r,mid);
if (ans<k) head=mid+1;
else tail=mid;
}
printf("%d\n",head-1);
break;
}
case 3:{
pos=in(); k=in();
SegTree_change(1,1,n,pos,k);
seq[pos]=k;
maxn=max(maxn,k);
break;
}
case 4:{
l=in(); r=in(); k=in();
ans=0;
SegTree_askpre(1,1,n,l,r,k);
printf("%d\n",ans);
break;
}
case 5:{
l=in(); r=in(); k=in();
ans=INF;
SegTree_asknext(1,1,n,l,r,k);
printf("%d\n",ans);
break;
}
}
}
}
Orz xym 优越的姿势