【BZOJ4825】单旋(AHOI&HNOI2017)-set+树状数组
测试地址:单旋
做法:本题需要用到set+树状数组。
首先,我们注意到题目中单旋只涉及到最小值和最大值,考虑到旋转是对称的,我们先只考虑单旋最小值的情况。
显然目标点每次都是往右旋转,往右旋转对点的影响是:该点的右子树深度没有影响,该点深度减少,而对于其它影响到的点深度都增加。那么我们可以得到一个非常优美的性质:每一次单旋,除了以目标点为根的子树外,其它所有点深度都增加。并且我们还能知道,进行一次这样的操作后树的形态也不会发生很大的改变,具体来说就是,目标点的右子树变成目标点父亲的左子树,目标点接到根的上面,这就说明树的形态是可以维护的。
接下来我们一一讨论涉及到的操作。首先是插入操作,你要找到这个点在DFS序上的前驱和后继,那么这个点的深度就是这两点深度的最大值(可以画个图理解一下),我们可以用set来维护DFS序。然后对于单旋操作,先把所有的点深度,因为我们知道它的子树不受影响,那么我们找到该子树对应的区间,把深度减掉,而对于目标点,则是把深度直接修改为。待删除的情况类似,我就不赘述了。这里主要讨论如何找到子树对应的区间,观察发现这棵子树对应的区间为DFS序上目标点和它的父亲之间的这些点。那么我们用set维护DFS序,因为涉及到的修改操作有区间修改和单点询问,可以差分一下用树状数组维护,同时也要维护树的具体结构。那么我们就做完了这一题,时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,op[100010],a[100010],cnt=0,sum[100010];
int fa[100010],ch[100010][2];
struct forsort
{
int id,val;
}f[100010];
set<int> S;
set<int>::iterator it;
bool cmp(forsort a,forsort b)
{
return a.val<b.val;
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&op[i]);
if (op[i]==1)
{
scanf("%d",&f[++cnt].val);
f[cnt].id=i;
}
}
sort(f+1,f+cnt+1,cmp);
for(int i=1;i<=cnt;i++)
a[f[i].id]=i;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int d)
{
for(int i=x;i<=n+1;i+=lowbit(i))
sum[i]+=d;
}
int query(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=sum[i];
return ans;
}
void Add(int s,int t,int d)
{
if (s>t) return;
add(s,d);
add(t+1,-d);
}
void modify(int x,int d)
{
int now=query(x);
Add(x,x,d-now);
}
void work()
{
int nowrt=0;
for(int i=1;i<=n;i++)
{
if (op[i]==1)
{
it=S.lower_bound(a[i]);
int ldep,rdep,lpos,rpos;
if (it!=S.end()) rdep=query(rpos=*it);
else rdep=rpos=0;
if (it!=S.begin())
{
it--;
ldep=query(lpos=*it);
}
else ldep=lpos=0;
ch[a[i]][0]=ch[a[i]][1]=0;
if (ldep>rdep)
{
fa[a[i]]=lpos;
if (lpos) ch[lpos][1]=a[i];
}
else
{
fa[a[i]]=rpos;
if (rpos) ch[rpos][0]=a[i];
}
S.insert(a[i]);
modify(a[i],max(ldep,rdep)+1);
printf("%d\n",max(ldep,rdep)+1);
if (!nowrt) nowrt=a[i];
}
if (op[i]==2||op[i]==4)
{
it=S.begin();
int v=*it;
printf("%d\n",query(v));
if (fa[v]) Add(v+1,fa[v]-1,-1);
else Add(v+1,n,-1);
if (fa[v]) ch[fa[v]][0]=ch[v][1];
if (ch[v][1])
{
fa[ch[v][1]]=fa[v];
if (!fa[v]) nowrt=ch[v][1];
}
if (op[i]==2)
{
Add(1,n,1);
fa[v]=0;
if (nowrt!=v)
{
ch[v][1]=nowrt;
fa[nowrt]=v;
}
nowrt=v;
modify(v,1);
}
else
{
S.erase(v);
if (nowrt==v) nowrt=ch[v][1];
}
}
if (op[i]==3||op[i]==5)
{
it=S.end();
it--;
int v=*it;
printf("%d\n",query(v));
if (fa[v]) Add(fa[v]+1,v-1,-1);
else Add(1,v-1,-1);
if (fa[v]) ch[fa[v]][1]=ch[v][0];
if (ch[v][0])
{
fa[ch[v][0]]=fa[v];
if (!fa[v]) nowrt=ch[v][0];
}
if (op[i]==3)
{
Add(1,n,1);
fa[v]=0;
if (nowrt!=v)
{
ch[v][0]=nowrt;
fa[nowrt]=v;
}
nowrt=v;
modify(v,1);
}
else
{
S.erase(v);
if (nowrt==v) nowrt=ch[v][0];
}
}
}
}
int main()
{
init();
work();
return 0;
}