Description
斜堆(skew heap)是一种常用的数据结构。它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值
都比它父亲大。因此在整棵斜堆中,根的值最小。但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任
何规定。在本题中,斜堆中各个元素的值均不相同。 在斜堆H中插入新元素X的过程是递归进行的:当H为空或者X
小于H的根结点时X变为新的树根,而原来的树根(如果有的话)变为X的左儿子。当X大于H的根结点时,H根结点的
两棵子树交换,而X(递归)插入到交换后的左子树中。 给出一棵斜堆,包含值为0~n的结点各一次。求一个结点
序列,使得该斜堆可以通过在空树中依次插入这些结点得到。如果答案不惟一,输出字典序最小的解。输入保证有
解。
HINT n<=200 , 其实复杂度为O(n ^ 2)
题解:(from Czarina)
性质1:若一个节点没有左儿子,则该节点一定没有右儿子。
因为右儿子是由左儿子旋转得到的,而在旋转的同时左边一定被插入节点了。
性质2:最后一次插入一定插到极左节点(一直往左走),显然得证。
性质3:最后一次插入节点一定没有右子树,因为它在插入时一定是将原来的某棵子树(可以为空)作为它的左子树。
所以最后插入的节点一定是满足性质2,3的深度最小的节点,删除该点后将其父亲到跟的左右子树交换。
因为如果不选深度最小的,则在交换的时候一定会交换到没有右子树的点,不满足性质1。
特例:若深度最小的满足性质2,3的节点有左儿子且其左儿子为叶子节点,则选其左儿子(字典序最小)
因为删除其左儿子后该点左右子树为空,满足性质1
所以就不断找最后加入的节点即可,记得考虑在删除根后的影响,还有边界的判定(编号从0开始要特判,我把0号节点变成了n + 1号)
**删除一个节点不但要将fa的儿子更新,还要将儿子的fa更新
提高代码能力,这种题应该1A
造数据可以自己构造一个序列,然后输出插入后的斜堆,保证一定有解。
思路:找性质,发现从后往前构造状态是确定的。构造题应该多找性质!**
#include<bits/stdc++.h>
using namespace std;
#define maxn 320
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define rep(i,l,r) for (int i = l ; i <= r ; i++)
int fa[maxn],ch[maxn][2];
int n,a[maxn],tot,root;
inline bool isleaf(int x){
return !ls(x) && !rs(x) && x;
}
int dfs(int x){
if ( isleaf(x) ) return x;
if ( !rs(x) && !isleaf(ls(x)) ) return x;
return dfs(ls(x));
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&n);
for (int i = 1 ; i <= n ; i++){
scanf("%d",&fa[i]);
if ( fa[i] >= 100 ){
fa[i] -= 100;
if ( !fa[i] ) fa[i] = n + 1;
rs(fa[i]) = i;
}
else{
if ( !fa[i] ) fa[i] = n + 1;
ls(fa[i]) = i;
}
}
root = n + 1;
for (int i = 1 ; i <= n + 1 ; i++){
int x = dfs(root);
a[i] = x;
if ( x == root ) root = ls(root) , fa[root] = 0;
else{
ls(fa[x]) = ls(x) , fa[ls(x)] = fa[x];
x = fa[x];
while ( x ){
swap(ls(x),rs(x));
x = fa[x];
}
}
}
for (int i = n + 1 ; i >= 1 ; i--){
if ( a[i] == n + 1 ) a[i] = 0;
printf("%d ",a[i]);
//if ( i > 1 ) printf(" ");
}
printf("\n");
}
附上datamaker
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 120
int ls[maxn],rs[maxn],fa[maxn],a[maxn],root;
int insert(int x,int y){
if ( !x ) return y;
if ( y < x ){
ls[y] = x;
return y;
}
swap(ls[x],rs[x]);
ls[x] = insert(ls[x],y);
return x;
}
int main(){
freopen("1.cnt","r",stdin);
int cnt;
scanf("%d",&cnt);
fclose(stdin);
freopen("1.cnt","w",stdout);
cout<<++cnt;
fclose(stdout);
srand(cnt + time(0));
freopen("input.txt","w",stdout);
int n = 7,k = rand() % 3 + 2;
cout<<n<<endl;
for (int i = 1 ; i <= n + 1 ; i++) a[i] = i;
random_shuffle(a + 1,a + n + 2);
// root = 1;
for (int i = 1 ; i <= n + 1 ; i++){
root = insert(root,a[i]);
}
for (int i = 1 ; i <= n + 1 ; i++){
if ( ls[i] ) fa[ls[i]] = i;
if ( rs[i] ) fa[rs[i]] = i + 100;
}
for (int i = 2 ; i <= n + 1 ; i++){
cout<<fa[i] - 1<<" ";
}
cout<<endl;
return 0;
}