偷懒的桐桐
题目描述
桐桐的老师布置桐桐写一个小根堆,但是桐桐不会堆的操作,所以想了一个偷懒的办法:
堆是一棵完全二叉树,每个结点有一个权。小根堆的根的权最小,且根的两个子树也是一个堆。可以用一个数组a来记录一棵完全二叉树,a[1]为根结点,若结点a[j]不是根结点,那么它的父亲为a[j div 2];若结点a[k]不是叶子结点,那么它的左儿子为a[2k],它的右儿子为a[2k+1]。
桐桐希望一组数据按一定顺序依次插入数组中(即第i个数为a[i]),最后得出来就已经是一个堆,即不需要任何交换操作,若有多种方法,输出字典序最大的一组,使得这个数据更乱。
输入
输入的第1行为一个正整数n,为插入的数字的个数;
第2行包含n个正整数,为所有需要插入的数字,数字之间用空格隔开。
为了简化题目,数据保证n=2k-1,即保证最后的堆是一棵满二叉树。
输出
输出包括1行,为插入的序列,数字之间用空格隔开,行末换行并没有空格。
样例输入
3
10 2 1
样例输出
1 10 2
提示
样例说明 1 2 10 与1 10 2都是满足要求的插入序列,但是1 10 2的字典序更大。
20%的数据:n≤7;
30%的数据:n≤15;
50%的数据:n≤1023;
100%的数:n≤65535,所有数字不超过108,且各不相同。
分析:构造字典序最大的二叉树;
预处理排序,第一个肯定是根;
接下来根据贪心原则,剩下的分成2半,第二半的第一个既是根的第一个儿子,第一半的第一个既是根的第二个儿子;
每一半继续递归即可;
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <algorithm> #include <climits> #include <cstring> #include <string> #include <set> #include <map> #include <queue> #include <stack> #include <vector> #include <list> #include <bitset> #define rep(i,m,n) for(i=m;i<=n;i++) #define rsp(it,s) for(set<int>::iterator it=s.begin();it!=s.end();it++) #define vi vector<int> #define pii pair<int,int> #define mod 1000000007 #define inf 0x3f3f3f3f #define pb push_back #define mp make_pair #define fi first #define se second #define ll long long #define pi acos(-1.0) const int maxn=1e6+10; const int dis[4][2]={{0,1},{-1,0},{0,-1},{1,0}}; using namespace std; ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);} ll qpow(ll p,ll q){ll f=1;while(q){if(q&1)f=f*p;p=p*p;q>>=1;}return f;} int n,m,a[maxn],ans[maxn]; void dfs(int now,int l,int r) { ans[now]=a[l]; if(l==r)return; int len=(r-l)/2; dfs(now*2+1,l+len+1,l+2*len); dfs(now*2+2,l+1,l+len); } int main() { int i,j,k,t; scanf("%d",&n); rep(i,0,n-1)scanf("%d",&a[i]); sort(a,a+n); dfs(0,0,n-1); printf("%d",ans[0]); rep(i,1,n-1)printf(" %d",ans[i]); printf(" "); //system ("pause"); return 0; }