[AH2017/HNOI2017]单旋
题目链接
https://www.luogu.com.cn/problem/P3721
做法
好神的题目!!!
我们重新修改一下链的定义,一条链为\(a_1,a_2,a_3,a_4,a_5...\),仅当\(a_{i}(∀i>1)\)为\(a_{i-1}\)的左儿子,或者右儿子。
不难发现,对于一条链,链底旋到链顶,其实只会把链底和其的某个儿子带到根节点,其余不变。
就在我思考不是链的情况,膜了一下题解,发现最大最小值到根节点一定是一条链!!!
且其最多只有一个儿子,要么左儿子,要么右儿子,且这两个儿子会在第一次旋转的时候过继给其父亲,并不会跟随其带动根当中。
所以每次\(splay\)只需要用线段树统计深度(怎么维护后面说),用\(rt\)储存根节点,然后记录一下\(fa\)和\(son\)数组即可反正每次最多修改几个,暴力修改即可。
还需要记住平衡树有个非常妙的性质,就是如果把原数组排序离散化,那么一个点的子树的中序遍历刚好就是这个数组上连续的一段,再看操作,例如:删除掉\(max\)值,其实就是把\(max\)的左儿子(也是其唯一的儿子)的子树深度全部\(-1\),那么对应在排序离散后的数组,其实就是这个子树所对应的连续的一段,直接用线段树维护每个位置的深度即可。
代码如下(貌似我是连\(x\)一同进行深度修改的):
inline void del_dian(int x)//x不能是根节点
{
int f=fa[x],w=son[f][0]==x?0:1;
int l=0,r=0;//修改的范围
if(w==0)l=x,r=f-1;//最小值
else l=f+1,r=x;//最大值
change1(1,1,qlen,l,r,-1);//深度减少1
fa[x]=0;
if(son[x][0])fa[son[x][0]]=f,son[f][w]=son[x][0];
else fa[son[x][1]]=f,son[f][w]=son[x][1];
}
当然,可能有人会问啦,每次插入一个数字,你怎么用线段树动态维护数组排序和离散化之后的数组呢?其实我们只要把最终的数组排序离散化一下,然后对于没出现过的数字不管他,然后数字出现单点修改即可。(因为代价只用单点查询)
但是插入到底会插入在哪个数字下面呢?
不难发现,要么前驱,要么后继(求前驱后继用set实现),这里又分两种情况了,要么一个在另外一个子树之内,要么在不同的子树,分类讨论即可。(只要运用好“把原数组排序离散化,那么一个点的子树的中序遍历刚好就是这个数组上连续的一段”的性质,这个操作应该不难想)
加入操作:
inline void add(int k,int father)
{
fa[k]=father;
if(father>k)son[father][0]=k;
else son[father][1]=k;
}
inline int ins(int x)
{
fuck.insert(x);++cnt;
if(!root){change2(1,1,qlen,x,1);root=x;return 1;}
else
{
set<int>::iterator pre,hou;
pre=fuck.lower_bound(x);
hou=fuck.upper_bound(x);
int deppre=999999999,dephou=999999999;
if(pre!=fuck.begin())
{
pre--;
deppre=findans(1,1,qlen,*pre);
}
if(hou!=fuck.end())dephou=findans(1,1,qlen,*hou);
int father;
if(deppre<dephou)//前面的节点是我的祖先
{
if(!son[*pre][1])add(x,*pre),father=*pre;
else add(x,*hou),father=*hou;
}
else
{
if(!son[*hou][0])add(x,*hou),father=*hou;
else add(x,*pre),father=*pre;
}
if(father==*hou)
{
change2(1,1,qlen,x,dephou+1);
return dephou+1;
}
else
{
change2(1,1,qlen,x,deppre+1);
return deppre+1;
}
}
}
所以,总的来说,时间复杂度就是\(O(nlogn)\)
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#define N 110000
#define NN 210000
using namespace std;
typedef long long LL;
int n;
struct node
{
int l,r,lazy;
}tr[NN];int len,rt;
inline void pushlazy(int x,int k){tr[x].lazy+=k;}
inline void downdata(int x)
{
if(tr[x].lazy)
{
pushlazy(tr[x].l,tr[x].lazy);pushlazy(tr[x].r,tr[x].lazy);
tr[x].lazy=0;
}
}
inline void bt(int l,int r)
{
int now=++len;
if(l<r)
{
int mid=(l+r)>>1;
tr[now].l=len+1;bt(l,mid);
tr[now].r=len+1;bt(mid+1,r);
}
else tr[now].lazy=0;
}
inline void change1(int now,int l,int r,int ll,int rr,int k)
{
if(l==ll && r==rr){pushlazy(now,k);return ;}
int mid=(l+r)>>1;
downdata(now);
if(rr<=mid)change1(tr[now].l,l,mid,ll,rr,k);
else if(mid<ll)change1(tr[now].r,mid+1,r,ll,rr,k);
else change1(tr[now].l,l,mid,ll,mid,k),change1(tr[now].r,mid+1,r,mid+1,rr,k);
}
inline void change2(int now,int l,int r,int k,int c)
{
if(l==r){tr[now].lazy=c;return ;}
int mid=(l+r)>>1;
downdata(now);
if(k<=mid)change2(tr[now].l,l,mid,k,c);
else change2(tr[now].r,mid+1,r,k,c);
}
inline int findans(int now,int l,int r,int k)
{
if(l==r)return tr[now].lazy;
int mid=(l+r)>>1;
downdata(now);
if(k<=mid)return findans(tr[now].l,l,mid,k);
else return findans(tr[now].r,mid+1,r,k);
}
int fa[N],son[N][2],root/*根节点*/,cnt,qlen;
set<int> fuck;
inline void add(int k,int father)
{
fa[k]=father;
if(father>k)son[father][0]=k;
else son[father][1]=k;
}
inline int ins(int x)
{
fuck.insert(x);++cnt;
if(!root){change2(1,1,qlen,x,1);root=x;return 1;}
else
{
set<int>::iterator pre,hou;
pre=fuck.lower_bound(x);
hou=fuck.upper_bound(x);
int deppre=999999999,dephou=999999999;
if(pre!=fuck.begin())
{
pre--;
deppre=findans(1,1,qlen,*pre);
}
if(hou!=fuck.end())dephou=findans(1,1,qlen,*hou);
int father;
if(deppre<dephou)//前面的节点是我的祖先
{
if(!son[*pre][1])add(x,*pre),father=*pre;
else add(x,*hou),father=*hou;
}
else
{
if(!son[*hou][0])add(x,*hou),father=*hou;
else add(x,*pre),father=*pre;
}
if(father==*hou)
{
change2(1,1,qlen,x,dephou+1);
return dephou+1;
}
else
{
change2(1,1,qlen,x,deppre+1);
return deppre+1;
}
}
}
inline void del_dian(int x)//x不能是根节点
{
int f=fa[x],w=son[f][0]==x?0:1;
int l=0,r=0;//修改的范围
if(w==0)l=x,r=f-1;//最小值
else l=f+1,r=x;//最大值
change1(1,1,qlen,l,r,-1);//深度减少1
fa[x]=0;
if(son[x][0])fa[son[x][0]]=f,son[f][w]=son[x][0];
else fa[son[x][1]]=f,son[f][w]=son[x][1];
}
inline int del(int x/*删除数字并且返回代价*/)
{
fuck.erase(x);
if(x==root)
{
if(son[x][0])root=son[x][0],fa[root]=0,son[x][0]=0;
else root=son[x][1],fa[root]=0,son[x][1]=0;
change1(1,1,qlen,1,qlen,-1);//全部深度减一
return 1;
}
else
{
int ans=findans(1,1,qlen,x);
del_dian(x);
return ans;
}
}
int q[N];int id[N];
inline bool cmp(int x,int y){return q[x]<q[y];}
int main()
{
// freopen("1.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int type;
scanf("%d",&type);
if(type!=1)q[i]=-type;
else
{
int x;scanf("%d",&x);
q[i]=x;
qlen++;id[qlen]=i;
}
}
sort(id+1,id+qlen+1,cmp);
for(int i=1;i<=qlen;i++)q[id[i]]=i;
bt(1,qlen);
int ans=0;
for(int i=1;i<=n;i++)
{
ans=0;
if(q[i]>0)ans+=ins(q[i]);
else
{
if(q[i]==-2)
{
int x=*fuck.begin();
ans+=del(x);
fuck.insert(x);
if(!root)root=x;
else fa[root]=x,son[x][1]=root,root=x;
change1(1,1,qlen,1,qlen,1);
change2(1,1,qlen,root,1);
}
else if(q[i]==-3)
{
int x=*(--fuck.end());
ans+=del(x);
fuck.insert(x);
if(!root)root=x;
else fa[root]=x,son[x][0]=root,root=x;
change1(1,1,qlen,1,qlen,1);
change2(1,1,qlen,root,1);
}
else if(q[i]==-4)ans+=del(*fuck.begin());
else ans+=del(*(--fuck.end()));
}
printf("%d\n",ans);
}
return 0;
}