把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ1078】[SCOI2008] 斜堆(模拟)

点此看题面

大致题意: 给定一个由\(0\sim n\)组成的斜堆,求字典序最小的插入序列。

模拟

一个清新自然的模拟做法~

考虑对于一个点,令\(pl,pr\)分别为它左子树、右子树的大小,可以分类讨论:

  • 如果\(pl=pr\),必然是先加入根,然后轮流往右子树、左子树中插入点。
  • 如果\(pl=pr+1\),必然是先加入根,然后轮流往左子树、右子树中插入点。
  • 否则,必然是先有一个堆,然后用新根替换掉了旧根,再轮流往子树中插入点。
    • 如果\(pl>pr+1\),说明左边有个旧堆,需要转化成上面第一种情况。
    • 如果\(pl<pr\),说明右边有个旧堆,需要转化为上面第二种情况。

如果我们对每个节点开一个数组存储该点的答案,直接递归做就可以实现\(O(n^2)\)的复杂度。

并且,这样的字典序必然是最小的。

所以\(n\le50\)的数据范围真的很迷。。。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,a[N+5],S[N+5][2],p[N+5],s[N+5][N+5];
I void dfs(CI x)//模拟
{
	RI l=S[x][0],r=S[x][1];l&&(dfs(l),0),r&&(dfs(r),0);
	RI i,pl=l?p[l]:0,pr=r?p[r]:0;if(pl>pr+1)//如果左边有个旧堆
	{
		for(i=1;i<=pl-pr;++i) s[x][++p[x]]=s[l][i];//存下旧堆中的点
		for(i=1;i<=pr;++i) s[l][i]=s[l][i+pl-pr];pl=pr;//修改左边的答案
	}
	if(pl<pr)//如果右边有个旧堆
	{
		for(i=1;i<=pr-pl+1;++i) s[x][++p[x]]=s[r][i];//存下旧堆中的点
		for(i=1;i<=pl-1;++i) s[r][i]=s[r][i+pr-pl+1];pr=pl-1;//修改右边的答案
	}
	if(pl==pr+1)//左、右轮流加
	{
		for(s[x][++p[x]]=x,i=1;i<=pr;++i) s[x][++p[x]]=s[l][i],s[x][++p[x]]=s[r][i];
		s[x][++p[x]]=s[l][pl];
	}
	else for(s[x][++p[x]]=x,i=1;i<=pr;++i) s[x][++p[x]]=s[r][i],s[x][++p[x]]=s[l][i];//右、左
}
int main()
{
	RI i,x;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),(x<100?S[x][0]:S[x-100][1])=i;//记下每个点的儿子
	for(dfs(0),i=1;i<=n+1;++i) printf("%d%c",s[0][i]," \n"[i==n+1]);return 0;
}
posted @ 2020-06-08 16:43  TheLostWeak  阅读(106)  评论(0编辑  收藏  举报