数据结构自由交配(更新中)
三道数据结构板子题。
点击查看
题意:维护一个数据结构,支持$5$种操作:- 将编号为\(x\)的数置顶
- 将编号为\(x\)的数置底
- 将编号为\(x\)的数提前/滞后\(1\)位
- 查询\(x\)排名
- 查询排名为\(x\)的数。
splay好题。
对于操作一,我们将\(x\)旋转到根,然后把它的左子树与后继合并;
对于操作二,我们将\(x\)旋转到根,然后把它的右子树与前驱合并;
对于操作三,我们将\(x\)和它的前驱/后继交换位置和信息;
对于操作四,我们将\(x\)旋转到根,然后输出\(siz-1\);
对于操作五,我们直接在树上查找即可。
#include<bits/stdc++.h>
using namespace std;
const int N=8e5+10;
int n,m,sz,rt=1;
int siz[N],pos[N],fa[N],c[N][2],v[N];
char ch[7];
inline int read()
{
int s=0,x=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')x=-1;ch=getchar();}
while(isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*x;
}
void pushup(int x)
{
siz[x]=siz[c[x][0]]+siz[c[x][1]]+1;
pos[v[c[x][0]]]=c[x][0],pos[v[c[x][1]]]=c[x][1];
}
void rotate(int &rt,int x)
{
int y=fa[x],z=fa[y],p,q=p;
p=(c[y][0]!=x);q=p^1;
if(y==rt)rt=x;
else
{
if(c[z][0]==y)c[z][0]=x;
else c[z][1]=x;
}
fa[x]=z;fa[y]=x;fa[c[x][q]]=y;
c[y][p]=c[x][q];c[x][q]=y;
pushup(y);pushup(x);
}
void splay(int &rt,int x)
{
int y,z;
while(x!=rt)
{
y=fa[x],z=fa[y];
if(y!=rt)
{
if((c[z][0]==y)^(c[y][0]==x))rotate(rt,x);
else rotate(rt,y);
}
rotate(rt,x);
}
pos[v[x]]=x;
}
void update(int x)
{
v[++sz]=x;siz[sz]=1;pos[x]=sz;
c[sz][0]=c[sz][1]=0;
if(sz>1)
{
c[sz-1][1]=sz;fa[sz]=sz-1;
splay(rt,sz);
}
}
int find(int x,int k)
{
int y=c[x][0];
if(siz[y]+1==k)return x;
else if(siz[y]+1<k)return find(c[x][1],k-siz[y]-1);
else return find(y,k);
}
void top(int x)
{
x=pos[x];
splay(rt,x);
if(!c[x][0])return ;
if(!c[x][1])c[x][1]=c[x][0],c[x][0]=0;
else
{
int y=find(rt,siz[c[x][0]]+2);
fa[c[rt][0]]=y;
c[y][0]=c[rt][0];
c[rt][0]=0;
splay(rt,y);
}
}
void insert(int s,int x)
{
if(!s)return ;int ps=pos[x];
splay(rt,ps);
int y=find(rt,s==1?siz[c[ps][0]]+2:siz[c[ps][0]]),z=v[y];
swap(pos[x],pos[z]);swap(v[ps],v[y]);
}
void bottom(int x)
{
x=pos[x];
splay(rt,x);
if(!c[x][1])return ;
if(!c[x][0])c[x][0]=c[x][1],c[x][1]=0;
else
{
int y=find(rt,siz[c[x][0]]);
fa[c[rt][1]]=y;
c[y][1]=c[rt][1];
c[rt][1]=0;
splay(rt,y);
}
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i)update(read());
while(m--)
{
scanf("%s",ch);
switch(ch[0])
{
case 'T':{
top(read());
break;
}
case 'B':{
bottom(read());
break;
}
case 'I':{
insert(read(),read());
break;
}
case 'A':{
int x=read();x=pos[x];
splay(rt,x);
printf("%d\n",siz[c[x][0]]);
break;
}
case 'Q':{
printf("%d\n",v[find(rt,read())]);
break;
}
}
}
return 0;
}
点击查看
题意:给定一个序列,对于第$i$次操作,找到第$i$小的数,输出现在的位置并将区间$[i,p_i]$翻转,若$p_i$已经在正确的位置,则将区间$[p_i,p_i]$翻转。上一个是splay,所以这次就用treap。
我们用treap维护剩下的区间最小值。对于每次操作,我们找到最小值,输出它,然后将左右儿子合并,打上翻转标记即可。
然后就没了。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+1;
int tot,n,m,stk[N],tp,rt;
int siz[N],w[N],son[N][2];
ll val[N];
bool lazy[N];
inline void update(int x){siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;}
void pushdown(int x)
{
swap(son[x][1],son[x][0]);
if(son[x][1])lazy[son[x][1]]^=1;
if(son[x][0])lazy[son[x][0]]^=1;
lazy[x]=0;
}
void build(int x)
{
while(tp&&val[x]<val[stk[tp]])son[x][0]=stk[tp--],update(son[x][0]);
if(tp)son[stk[tp]][1]=x;
stk[++tp]=x;
}
int merge(int x,int y)
{
if(!x||!y)return x|y;
if(val[x]<=val[y])
{
if(lazy[x])pushdown(x);
son[x][1]=merge(son[x][1],y);
update(x);
return x;
}
if(lazy[y])pushdown(y);
son[y][0]=merge(x,son[y][0]);
update(y);
return y;
}
void split(int x)
{
int l=son[x][0],r=son[x][1];
son[x][0]=son[x][1]=0;
lazy[l]^=1;
rt=merge(l,r);
}
int l,r,a,b,c;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
int x;scanf("%d",&x);
val[i]=1ll*x*n+i;siz[i]=1;
build(i);
}
while(tp)update(stk[tp--]);
rt=stk[1];
for(int i=1;i<=n;++i)
{
if(lazy[rt])pushdown(rt);
printf("%d ",siz[son[rt][0]]+i);
split(rt);
}
return 0;
}
点击查看
题意:给定一个序列,初始为零号,有两种操作\((t,s,x)\):
- \(t=1\)时,将\(s\)号序列下标大于\(x\)的数删除,删除的数构成新序列;
- \(t=2\)时,将\(s\)号序列大于\(x\)的数删除,删除的数构成新序列。
主席树板子题。
可以发现,操作1就是将序列按下标分为一段前缀和一段后缀,并将后缀加入到新序列中;操作2就是将序列按值域分为前缀和后缀,并将后缀加入到新序列中。
我们用四个数组l,r,minn,maxn表示区间l到r中值域为minn到maxn的数。
知道上面的信息后可以直接套主席树板子求出新序列,那么现在的主要问题就变为了如何处理修改操作。
对于操作2,直接将原序列复制一份到新序列,并将原序列的maxn改为x,新序列的minn改为x+1即可。
对于操作1,我们可以发现,其实就是求序列中大于等于minn小于等于maxn的第k小,经典主席树问题。主席树套二分可以解决。
复杂度\(O(nlog^2n)\)。
#include<bits/stdc++.h>
#define l(p) tr[p].l
#define r(p) tr[p].r
#define sum(p) tr[p].sum
#define L(p) t[p].L
#define R(p) t[p].R
#define mx(p) t[p].maxn
#define mi(p) t[p].minn
using namespace std;
const int N=2e5+10;
struct node{
int l,r,sum;
}tr[N<<3];
struct seg{
int L,R,maxn,minn;
}t[N];
int n,q,rt[N],cnt;
int build(int p,int l,int r,int x)
{
int st=++cnt;
tr[st]=tr[p];
if(l==r)
{
sum(st)++;
return st;
}
int mid=l+r>>1;
if(x<=mid)l(st)=build(l(st),l,mid,x);
else r(st)=build(r(st),mid+1,r,x);
sum(st)=sum(l(st))+sum(r(st));
return st;
}
int query(int x,int y,int l,int r,int mik,int mak)
{
if(mak<mik)return 0;
if(l>=mik&&r<=mak)return sum(x)-sum(y);
int mid=l+r>>1,ans=0;
if(mik<=mid)ans+=query(l(x),l(y),l,mid,mik,mak);
if(mak>mid)ans+=query(r(x),r(y),mid+1,r,mik,mak);
return ans;
}
inline int read()
{
int s=0,x=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')x=-1;ch=getchar();}
while(isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*x;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)rt[i]=build(rt[i-1],1,n,read());
q=read();
for(int i=1;i<=q;++i)
{
int opt=read(),o=read(),p=read();
t[i]=t[o];
if(opt==1)
{
int L=L(o)-1,R=R(o);
while(L<R)
{
int mid=L+R>>1;
if(query(rt[mid],rt[l(o)-1],1,n,mi(o),mx(o))<=p)L=mid+1;
else R=mid;
}
L(i)=L+1;
R(o)=L;
}
else
{
mi(i)=max(mi(i),p+1);
mx(o)=min(mx(o),p);
}
printf("%d\n",max(0,query(rt[r(i)],rt[l(i)-1],1,n,mi(i),mx(i))));
}
return 0;
}

几道数据结构例题
浙公网安备 33010602011771号