pku 1785 Binary Search Heap Construction(笛卡尔树/Treap)
转个分析:
题目给出的是一棵具有两个关键字的树,于是很自然地往Treap的方向去做了。结果成功的在 #15 TLE了。原因是这样构建Treap,给定的A[]已经不是随机的了,很容易退化成单链,造成O(N^2)的复杂度。
由于题目的特殊性(都怪我忽视了题目名称。。。),我们可以直接用构造笛卡尔树的方法来解决这个问题。
1、 要求的二叉树T的结点K值构成排序二叉树óT的中序遍历的各结点的K值递增。
2、 把K值排序后即可得到T的中序遍历{P1,P2,…Pn};
3、 问题转化为:n个结点1~n,每个结点仅有一个权值Bi{Bi=APi},要求建一个堆,并且中序遍历为1~n。排序K值后从最右的路径插入结点,如果结点的A值大于A[i],则旋转,否则直接作为右结点插入。(实际上是Treap的左旋操作)
实际的操作很简单:排序->从第二个结点开始插入:链在最右边,递归左旋->由映射恢复原序->输出。
排序的复杂度为O(NlogN),插入结点的复杂度为O(N),因为每个结点最多只是进出一次,所以总的复杂度是O(NlogN)的。要注意的是需要构建结点的映射关系,以便最后按原序输出。
#include<stdio.h>
#include<string.h>
#include <algorithm>
using namespace std;
#define MAXKEY 50
#define MAXNSIZE 50010
struct CTNode //Cartesian_Tree node
{
CTNode() {}
char key[MAXKEY];
int priority;
int parent,l,r;
};
CTNode node[MAXNSIZE];
inline bool mycmp(const CTNode &n1,const CTNode &n2)
{
return strcmp(n1.key,n2.key)<0;
}
void insert(int x,int inidx)
{
node[inidx].parent=node[inidx].l=node[inidx].r=-1;
while(x!=-1)
{
if(node[inidx].priority > node[x].priority)
{
//左旋
node[inidx].parent=node[x].parent;
node[x].r=node[inidx].l;
node[inidx].l=x;
x=node[x].parent;
}
else
{
node[inidx].parent=x;
node[x].r=inidx;
return;
}
}
}
void display(int root)
{
printf("(");
if(node[root].l != -1) display(node[root].l);
printf("%s/%d",node[root].key,node[root].priority);
if(node[root].r != -1) display(node[root].r);
printf(")");
}
int main()
{
int n,i,p,k;
while(scanf("%d",&n),n)
{
for(k=0; k<n; k++)
{
scanf("%s",node[k].key);
for(i=0; node[k].key[i]!='/'; i++) ;
node[k].key[i++]=0;
p=0;
while(node[k].key[i]) p=p*10+node[k].key[i++]-'0';
node[k].priority=p;
}
sort(node,node+n,mycmp);
int root=0,last=0;
node[root].parent=node[root].l=node[root].r=-1;
for(i=1; i<n; i++)
{
insert(last,i);
if(node[i].parent==-1) root=i;
last=i;
}
display(root);
printf("\n");
}
return 0;
}
浙公网安备 33010602011771号