bzoj 1078 //1078:[SCOI2008]斜堆 //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1078
更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录
bzoj 1078 2019-10-24
824 kb | 72 ms | C++/Edit | 1246 B |
//1078:[SCOI2008]斜堆
//在线测评地址https://www.luogu.org/problem/P2475
//边读题,边按题意画图,更容易加速理解题意.2019-10-1 7:28
样例1执行过程如下。2019-10-1 9:46
输入 #1
6
100 0 101 102 1 2
输出 #1
0 1 2 3 4 5 6
样例2执行过程如下。2019-10-1 15:09
输入 #2
6
100 0 2 102 4 104
输出 #2
4 6 5 2 0 1 3
样例3执行过程如下。2019-10-1 15:34
输入 #3
7
0 100 1 102 2 3 5
输出 #3
2 5 0 3 4 6 7 1
//此文http://www.cppblog.com/MatoNo1/archive/2013/03/03/192131.html思路不错,摘抄如下
/*
一开始想傻了囧……不过很快就发现这其实是个超级大水题……
考虑斜堆中最后插入的那个结点,容易发现:
(1)它一定是一个极左结点(就是从根往它的路上一直都是沿着左链走),因为插入的时候每次都是插入到左子树中;
(2)它一定木有右子树,因为插入的时候每次都是把原来的某棵子树作为新结点的左子树;
满足(1)(2)的结点可能有多个,但紧接着可以发现,这个斜堆中的每个结点如果木有左子结点,那么也木有右子结点(或者说,每个非叶结点都有左子树),而在插入一个结点之前,其所有的祖先都被交换了左右子树,所以,若新结点的祖先中有满足(1)(2)的,且新结点不是叶结点,那么在新结点插入之前,这个满足(1)(2)的祖先必然是只有右子树而木有左子树的,这与上面的那个性质矛盾,所以,可以得出:最后插入的那个结点一定是满足(1)(2)的结点中,深度最小的那个(设为X),除非X的左子结点是叶结点,此时为了满足字典序最小,应该取X的左子结点为最后插入的。找到这个最后插入的结点以后,只需要把它删掉,并把它的所有祖先交换左右子树,就是插入该结点以前的状态了。这样可以找到字典序最小的插入顺序。
*/
//此文http://hzwer.com/5790.html代码写得不错.
//3个样例通过,提交AC.2019-10-2 10:03
#include <stdio.h>
#include <string.h>
#define maxn 55
int fa[maxn],ls[maxn],rs[maxn],n;//fa父 ls左子 rs右子
int ans[maxn],top=0,root=0;//栈
void swap(int *a,int *b){//交换
int t;
t=*a,*a=*b,*b=t;
}
void init(){
int i,x;
memset(fa,-1,sizeof(fa)),memset(ls,-1,sizeof(ls)),memset(rs,-1,sizeof(rs));
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&x);
if(x<100)fa[i]=x,ls[x]=i;
else x-=100,fa[i]=x,rs[x]=i;
}
}
void solve(){
int x=root,t,f,s;//自顶向底开始插入
while(rs[x]!=-1)x=ls[x];//找深度最小,没有右子树的左节点
t=ls[x];//t是x的左叶子节点,该叶节点可能不存在.
if(t!=-1&&ls[t]==-1&&rs[t]==-1)x=t;//存在叶节点t
ans[++top]=x;//记录插入的节点x
//删除插入的x节点
if(x==root)root=ls[x],fa[root]=-1;//x若是根节点
else{
f=fa[x],s=ls[x],ls[f]=s,fa[s]=f;//建立x节点的父子联系
while(f!=-1)swap(&ls[f],&rs[f]),f=fa[f];
}
}
int main(){
int i;
init();
for(i=1;i<=n+1;i++)solve();
while(top)printf("%d ",ans[top--]);
return 0;
}