SGU 155.Cartesian Tree

时间限制:0.25s

空间限制:6M

题意:

      给出n(n< 50000)个含双关键字(key,val)的节点,构造一颗树使该树,按key值是一颗二分查找树,按val值是一个小根堆.

 

 


 

Solution :

             先按k值从小到大排序.

             再从序列中找到最小的val值,将其作为根.再对它的左边和右边做同样的操作.左边最大的数做左儿子,右边做右儿子。递归即可.

             这里要快速找到一个序列区间的最大值,用ST方法求RMQ就行了.

             时间复杂度O(nlogN),空间复杂度O(n)

 

code:

 

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <functional>

#include <vector>

#include <utility>

using namespace std;



struct node {

	int key, val, ID;

} p;



struct answer {

	int fa, lson, rson;

} ans[51000];



typedef pair<int , int > P;

vector<node> f;

P st[51000][20];

int n, x, y;



bool cmp (node a, node b) {

	return a.key < b.key;

}

//ST RMQ

void ST() {

	for (int i = n - 1; i >= 0 ; i--)

		for (int j = 1; i + (1 << j) <= n; j++)

              {

			if (st[i][j - 1].first < st[i + (1 << j - 1)][j - 1].first)

				st[i][j] = st[i][j - 1];

			else

				st[i][j] = st[i + (1 << j - 1)][j - 1];

		}

}

//得到区间最小值的位置

int getmin (int l, int r)

{

	P tem;

	tem=st[l][0];

	for (int k = 0; l + (1 << k) <= r; k++)

       {

		if (st[l][k].first < tem.first)

			tem = st[l][k];

		if (st[r - (1 << k) + 1][k].first < tem.first)

			tem = st[r - (1 << k) + 1][k];

	}

	return tem.second;

}

//递归建树

int make (int l, int r, int fa)

{

	int k = getmin (l, r);

	int pos = f[k].ID;

	ans[pos].fa = fa;

	if (l >= r) return pos;

	if (l < k) ans[pos].lson = make (l, k - 1, pos);

	if (k < r) ans[pos].rson = make (k + 1, r, pos);

	return pos;

}



int main()

{

	scanf("%d",&n);

	for (int i = 0; i < n; i++)

	{

		scanf("%d %d",&x,&y);

		p.key = x, p.val = y, p.ID = i + 1;

		f.push_back (p);

	}

	//按key值从小到大排序

	sort (f.begin(), f.end(), cmp);

	for (int i = 0; i < (int) f.size(); i++)

		st[i][0] = make_pair (f[i].val, i);

	ST();

	make (0, n - 1, 0);

	//一定有解直接输出 "YES"

	puts("YES\n");

	for (int i = 1; i <= n; i++)

              printf("%d %d %d\n",ans[i].fa,ans[i].lson,ans[i].rson);

	return 0;

}

 

  

 

  

你可能感兴趣的:(tree)