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
");
	for (int i = 1; i <= n; i++)
              printf("%d %d %d
",ans[i].fa,ans[i].lson,ans[i].rson);
	return 0;
}

  

  

原文地址:https://www.cnblogs.com/keam37/p/3864681.html