bzoj1078: [SCOI2008]斜堆
阿新 • • 發佈:2018-12-13
考慮斜堆中最後插入的那個結點,容易發現:
(1)它一定是一個極左結點(就是從根往它的路上一直都是沿著左鏈走),因為插入的時候每次都是插入到左子樹中;
(2)它一定木有右子樹,因為插入的時候每次都是把原來的某棵子樹作為新結點的左子樹;
滿足(1)(2)的結點可能有多個,但緊接著可以發現,這個斜堆中的每個結點如果木有左子結點,那麼也木有右子結點(或者說,每個非葉結點都有左子樹),而在插入一個結點之前,其所有的祖先都被交換了左右子樹,所以,若新結點的祖先中有滿足(1)(2)的,且新結點不是葉結點,那麼在新結點插入之前,這個滿足(1)(2)的祖先必然是隻有右子樹而木有左子樹的,這與上面的那個性質矛盾,所以,可以得出:最後插入的那個結點一定是滿足(1)(2)的結點中,深度最小的那個(設為X),除非X的左子結點是葉結點,此時為了滿足字典序最小,應該取X的左子結點為最後插入的。找到這個最後插入的結點以後,只需要把它刪掉,並把它的所有祖先交換左右子樹,就是插入該結點以前的狀態了。這樣可以找到字典序最小的插入順序。
#include<bits/stdc++.h>
using namespace std;
int fa[52],ls[52],rs[52],n,i,st[52],top,x,rt;
inline char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
int x=0,fl=1;char ch=gc();
for (;ch<48||ch> 57;ch=gc())if(ch=='-')fl=-1;
for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*fl;
}
inline void wri(int a){if(a>=10)wri(a/10);putchar(a%10|48);}
void solve(){
int x=rt;
while (rs[x]!=-1) x=ls[x];
int t=ls[x];
if (t!=-1 && ls[t]==-1 && rs[t] ==-1) x=t;
st[++top]=x;
if (x==rt) rt=ls[x];
if (fa[x]!=-1) fa[ls[x]]=fa[x],ls[fa[x]]=ls[x];
x=fa[x];
while (x!=-1) swap(ls[x],rs[x]),x=fa[x];
}
int main(){
n=rd();
fa[0]=-1;
memset(ls,-1,sizeof(ls));
memset(rs,-1,sizeof(rs));
for (i=1;i<=n;i++){
x=rd();
if (x<100) ls[x]=i,fa[i]=x;
else rs[x-100]=i,fa[i]=x-100;
}
for (i=1;i<=n+1;i++) solve();
wri(st[top]);
for (i=top-1;i;i--) putchar(' '),wri(st[i]);
}