bzoj3224 Tyvj 1728 普通平衡树
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3224
题意:
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
数据范围
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
题解:
基础平衡树操作。
旧代码。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
int n;
int root=0;
int fa[N],ch[N][2],size[N],cnt[N],key[N];
int sz=0;
inline void update(int k)
{
if(k)
{
size[k]=cnt[k];
if(ch[k][0]) size[k]+=size[ch[k][0]];
if(ch[k][1]) size[k]+=size[ch[k][1]];
}
return;
}
inline bool get(int x){return ch[fa[x]][1]==x;}
inline void rotate(int x,int &k)
{
int y=fa[x],z=fa[y];
int l,r;
if(ch[y][0]==x) l=0;else l=1;r=l^1;
if(y==k) k=x;
else
{
if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;
}
ch[y][l]=ch[x][r]; fa[ch[x][r]]=y;
ch[x][r]=y; fa[x]=z; fa[y]=x;
update(y);update(x);
return;
}
inline void splay(int x,int &k)
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if(x==ch[y][0]^y==ch[z][0]) rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
return;
}
inline int findx(int x)
{
int now=root;
while(1)
{
if(ch[now][0]&&x<=size[ch[now][0]]) now=ch[now][0];
else
{
int tmp=cnt[now]+(ch[now][0]?size[ch[now][0]]:0);
if(x<=tmp) return key[now];
x-=tmp; now=ch[now][1];
}
}
}
inline void insert(int x)
{
if(root==0) { sz++;size[sz]=1;ch[sz][0]=ch[sz][1]=0;root=sz;cnt[sz]=1;fa[sz]=0;key[sz]=x; return; }
int now=root,f=0;
while(1)
{
if(key[now]==x)
{size[now]++;cnt[now]++;update(f);splay(now,root); break;}
if(x<key[now]) { f=now;now=ch[now][0];}
else{ f=now; now=ch[now][1];}
if(now==0)
{
sz++;now=sz;size[sz]=1;cnt[sz]=1;ch[sz][0]=ch[sz][1]=0;fa[sz]=f;key[sz]=x;
if(x<key[f]) ch[f][0]=sz; else ch[f][1]=sz;
update(f); splay(now,root);break;
}
}
return;
}
inline int find(int x)
{
int now=root,ans=0;
while(1)
{
if(x<key[now]) now=ch[now][0];
else
{
ans+=(ch[now][0]?size[ch[now][0]]:0);
if(key[now]==x){ splay(now,root); return ans+1;}
ans+=cnt[now]; now=ch[now][1];
}
}
}
inline void clear(int k)
{
ch[k][0]=ch[k][1]=key[k]=cnt[k]=size[k]=fa[k]=0;return;
}
inline int getpre()
{
int now=ch[root][0];
while(ch[now][1]) now=ch[now][1];
return now;
}
inline int getnxt()
{
int now=ch[root][1];
while(ch[now][0]) now=ch[now][0];
return now;
}
inline void de(int x)
{
int now=root,f=0;
if(root==0) return;
int sss=find(x);
if(cnt[root]>1){cnt[root]--; update(root);return;}
if(!ch[root][0]&&!ch[root][1]) {clear(root); root=0;return;} //注意改根清空。
if(!ch[root][0]) {int oldroot=root;root=ch[oldroot][1]; fa[root]=0; clear(oldroot);return;}
if(!ch[root][1]) {int oldroot=root;root=ch[oldroot][0]; fa[root]=0; clear(oldroot);return;}
else
{
int pre=getpre();
int oldroot=root;
splay(pre,root);
fa[ch[oldroot][1]]=root; ch[root][1]=ch[oldroot][1];
clear(oldroot);update(root);
return;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
switch(a){
case 1: insert(b); break;
case 2: de(b); break;
case 3: printf("%d\n",find(b)); break;
case 4: printf("%d\n",findx(b)); break;
case 5: {
insert(b);
printf("%d\n",key[getpre()]);
de(b);
break;
}
case 6: {
insert(b);
printf("%d\n",key[getnxt()]);
de(b);
break;
}
}
}
return 0;
}
bzoj3223 Tyvj 1729 文艺平衡树
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3223
题意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
数据范围
N,M<=100000
题解:
基础splay操作。
旧代码。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;
int fa[N],ch[N][2],id[N],size[N];
int n,m,root;
bool rev[N];
void update(int k)
{
size[k]=size[ch[k][0]]+size[ch[k][1]]+1;
}
void build(int l,int r,int f)
{
if(l>r) return;
int now=id[l];int last=id[f];
if(l==r)
{
size[now]=1;rev[now]=0;fa[now]=last;
if(l<f) ch[last][0]=now;
else ch[last][1]=now;
return;
}
else
{
int mid=(l+r)>>1;
now=id[mid];
build(l,mid-1,mid);build(mid+1,r,mid);
rev[now]=0; fa[now]=last;
if(mid<f) ch[last][0]=now;
else ch[last][1]=now;
update(now);
}
}
void pushdown(int k)
{
if(rev[k])
{
swap(ch[k][0],ch[k][1]);
rev[ch[k][0]]^=1;
rev[ch[k][1]]^=1;
rev[k]=0;
}
}
void rotate(int x,int &k)
{
int y=fa[x]; int z=fa[y];
int l,r;
if(ch[y][0]==x) l=0; else l=1; r=l^1;
if(y==k) k=x;
else
{if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;}
fa[x]=z; fa[y]=x; fa[ch[x][r]]=y;
ch[y][l]=ch[x][r];ch[x][r]=y;
update(y);update(x);
}
void splay(int x,int &k) //传地址的意义 在于直接改变root
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if(ch[y][0]==x^ch[z][0]==y)
rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
}
int find(int k,int rank)
{
pushdown(k);
int l=ch[k][0];int r=ch[k][1];
if(size[l]+1==rank) return k;
if(size[l]>=rank) return find(l,rank);
else
return find(r,rank-size[l]-1);
}
void rever(int l,int r)
{
int x=find(root,l);int y=find(root,r+2);
splay(x,root);
splay(y,ch[x][1]);
int z=ch[y][0];
rev[z]^=1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n+2;i++)
id[i]=i;
root=(n+3)>>1;
build(1,n+2,0);
int l,r;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
rever(l,r);
}
for(int i=1;i<=n;i++) printf("%d ",find(root,i+1)-1);
return 0;
}
bzoj3196 Tyvj 1730 二逼平衡树
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3196
题意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
数据范围
1.n和m的数据范围:n,m<=50000
2.序列中每个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操作的k可能为负数
题解:
线段树套splay(大常数组合orz)
线段树的每一个节点建一个该区间的平衡树。(空间O(nlogn))
opt1:查询覆盖该区间的线段树节点对应平衡树中比k小的个数+1
opt2:二分这个数,转化为opt1
opt3:该点向上一条链的线段树平衡树中都删除一个这个点对应的值,插入一个这个点的新值。
opt4:查询覆盖该区间的线段树节点对应平衡树中该值前驱并取最大。
opt5:查询覆盖该区间的线段树节点对应平衡树中该值后并取最小。
(200行
+
的代码_ (:P」∠)_ )
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50005;
const int NN=2000005;
const int inf=0x3f3f3f3f;
struct node
{
int ch[2],fa,val,size,cnt;
void init()
{
ch[0]=ch[1]=fa=val=size=cnt=0;
}
}tr[NN];
struct NODE
{
int ls,rs,root;
void init()
{
ls=rs=root=0;
}
}TR[2*N];
int n,m,tail=0,TAIL=0,a[N],root;
void update(int nd)
{
int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];
tr[nd].size=tr[nd].cnt;
if(ls) tr[nd].size+=tr[ls].size;
if(rs) tr[nd].size+=tr[rs].size;
}
int insert(int &nd,int fa,int val)
{
if(!nd) {nd=++tail; tr[nd].init(); tr[nd].fa=fa; tr[nd].size=tr[nd].cnt=1; tr[nd].val=val; return nd;}
int pos;
if(val<tr[nd].val) pos=insert(tr[nd].ch[0],nd,val);
else if(val>tr[nd].val) pos=insert(tr[nd].ch[1],nd,val);
else {tr[nd].cnt++; pos=nd;}
update(nd);
return nd;
}
void rotate(int x,int &top)
{
int y=tr[x].fa; int z=tr[y].fa;
if(y==top) top=x;
else if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;
tr[x].fa=z;
int l= (tr[y].ch[0]==x)? 0:1; int r=l^1;
tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
tr[x].ch[r]=y; tr[y].fa=x;
update(y); update(x);
}
void splay(int x,int &top)
{
while(x!=top)
{
int y=tr[x].fa; int z=tr[y].fa;
if(y!=top)
{
if(tr[y].ch[0]==x ^ tr[z].ch[0]==y) rotate(x,top);
else rotate(y,top);
}
rotate(x,top);
}
}
void build(int &nd,int lf,int rg)
{
nd=++TAIL;
for(int i=lf;i<=rg;i++) {int pos=insert(TR[nd].root,0,a[i]); splay(pos,TR[nd].root);}
if(lf==rg) return;
int mid=(lf+rg)>>1;
build(TR[nd].ls,lf,mid);
build(TR[nd].rs,mid+1,rg);
}
int query(int nd,int val)
{
if(!nd) return 0;
if(tr[nd].val==val) return tr[tr[nd].ch[0]].size;
else if(tr[nd].val<val) return tr[tr[nd].ch[0]].size+tr[nd].cnt+query(tr[nd].ch[1],val);
else return query(tr[nd].ch[0],val);
}
int query(int nd,int lf,int rg,int L,int R,int val)
{
if(L<=lf&&rg<=R) return query(TR[nd].root,val);
int ans=0;
int mid=(lf+rg)>>1;
if(L<=mid) ans+=query(TR[nd].ls,lf,mid,L,R,val);
if(R>mid) ans+=query(TR[nd].rs,mid+1,rg,L,R,val);
return ans;
}
int find(int nd,int val)
{
if(!nd) return 0;
int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];
if(tr[nd].val==val) return nd;
else if(val<tr[nd].val) return find(tr[nd].ch[0],val);
else return find(tr[nd].ch[1],val);
}
int getpre(int x)
{
int tmp=tr[x].ch[0];
while(tr[tmp].ch[1]) tmp=tr[tmp].ch[1];
return tmp;
}
void del(int &x)
{
if(tr[x].cnt>1) {tr[x].cnt--; update(x); return;}
if(!tr[x].ch[0]&&!tr[x].ch[1]) { tr[x].init(); x=0; return;}
else if(!tr[x].ch[0]||!tr[x].ch[1])
{
int l= tr[x].ch[0]==0? tr[x].ch[1]:tr[x].ch[0];
tr[x].init(); x=l; tr[x].fa=0; update(x); return;
}
int now=x; int pre=getpre(now);
splay(pre,x);
tr[pre].ch[1]=tr[now].ch[1];
tr[tr[now].ch[1]].fa=pre;
tr[now].init();
update(pre);
return;
}
void modify(int &nd,int pos,int val)
{
int pv=a[pos];
int x=find(nd,pv);
splay(x,nd); del(nd);
int now=insert(nd,n,val); splay(now,nd);
}
void modify(int nd,int lf,int rg,int pos,int val)
{
modify(TR[nd].root,pos,val);
if(lf==rg) return;
int mid=(lf+rg)>>1;
if(pos<=mid) modify(TR[nd].ls,lf,mid,pos,val);
if(pos>mid) modify(TR[nd].rs,mid+1,rg,pos,val);
}
int preval(int nd,int val)
{
int tmp=nd; int ans=-inf;
while(tmp)
{
if(tr[tmp].val<val)
{
ans=max(tr[tmp].val,ans);
tmp=tr[tmp].ch[1];
}
else tmp=tr[tmp].ch[0];
}
return ans;
}
int nxtval(int nd,int val)
{
int tmp=nd; int ans=inf;
while(tmp)
{
if(tr[tmp].val>val)
{
ans=min(tr[tmp].val,ans);
tmp=tr[tmp].ch[0];
}
else tmp=tr[tmp].ch[1];
}
return ans;
}
int query_pre(int nd,int lf,int rg,int L,int R,int val)
{
if(L<=lf&&rg<=R) return preval(TR[nd].root,val);
int ans=-inf;
int mid=(lf+rg)>>1;
if(L<=mid) ans=max(ans,query_pre(TR[nd].ls,lf,mid,L,R,val));
if(R>mid) ans=max(ans,query_pre(TR[nd].rs,mid+1,rg,L,R,val));
return ans;
}
int query_nxt(int nd,int lf,int rg,int L,int R,int val)
{
if(L<=lf&&rg<=R) return nxtval(TR[nd].root,val);
int ans=inf;
int mid=(lf+rg)>>1;
if(L<=mid) ans=min(ans,query_nxt(TR[nd].ls,lf,mid,L,R,val));
if(R>mid) ans=min(ans,query_nxt(TR[nd].rs,mid+1,rg,L,R,val));
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
tr[0].init(); build(root,1,n);
while(m--)
{
int opt,l,r,pos,k; scanf("%d",&opt);
if(opt==1) //查询k在区间[l,r]内的排名
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",query(root,1,n,l,r,k)+1);
}
else if(opt==2)//之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
{
scanf("%d%d%d",&l,&r,&k);
int lf=0; int rg=100000000;
while(lf+1<rg)
{
int mid=(lf+rg)>>1;
if(query(root,1,n,l,r,mid)+1<=k) lf=mid;
else rg=mid;
}
if(query(root,1,n,l,r,rg)+1<=k) printf("%d\n",rg);
else printf("%d\n",lf);
}
else if(opt==3)//之后有两个数pos,k 表示将pos位置的数修改为k
{
scanf("%d%d",&pos,&k);
modify(root,1,n,pos,k);
a[pos]=k;
}
else if(opt==4)
{
scanf("%d%d%d",&l,&r,&k); //之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
printf("%d\n",query_pre(root,1,n,l,r,k));
}
else if(opt==5)
{
scanf("%d%d%d",&l,&r,&k); //之后有三个数l,r,k 表示查询区间[l,r]内k的后驱
printf("%d\n",query_nxt(root,1,n,l,r,k));
}
}
return 0;
}