给出节点两个关键值,按主关键值成BST,按副关键值成Heap——其实就是把节点排成Treap。只需要建立,不需要删除。
  Submit 1: TLE on 15。修改一下结构吧。
  Submit 2: TLE on 15。上述算法有可能退化到O(n^2)。
  如果给出的节点按k值递增,那么构造Cartesian Tree有O(n)的算法(From 周源):
  首先可以认为在[1, 1]范围内建立一颗笛卡尔树是“平凡”的:不过是一个根节点而已。假设已经在[1, i-1]范围内建立了一个笛卡尔树,(i>1),那么我们试图得到[1, i]范围内的笛卡尔树。由于这颗树中有排序树的性质,所以可以沿着根节点不停的向右孩子走,直到无路可走,就把第i个节点挂在当且节点的右边:但是这样显然不一定会满足“二叉堆”的性质。如果第i个点的a值足够的大,那么二叉堆的性质并不会被破坏;但是如果小于其父亲的a值,在二叉堆的算法中,就需要一个shift(i)过程,但在这里会破坏排序树的美好性质。所以,我们就必须使用平衡排序二叉树算法中的左旋过程:Left-Rotate[father(i)],对其进行一次左旋,就可以消除冲突。但是此时i点上升了一层,可能会和原来i的父亲的父亲产生冲突,那么继续进行左旋,直到没有冲突,就得到了新的笛卡尔树了。
  乍一看这个算法,每个点可能最多会被左旋N次,最坏情况下的复杂度岂不是O(N^2)。但事实上,从“平摊”的角度分析,考察这颗动态树的最右边那条链,每个点最多会进入一次右链,也就最多会出一次。如果维护一个最右边的节点,那么插入一个节点的时间复杂度为O(1),而某一个节点退出右链的时间复杂度显然是O(1)。所以整个算法的时间复杂度就是O(N)。
  原题给出的数据是无序的,但可以用O(nlogn)的排序算法,将整体复杂度稳定在O(nlogn)。
  Submit 3: AC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <cstdio>
#include <algorithm>
using namespace std;
 
struct node
{
    int id,k,a;
};
 
bool operator <(const node &a,const node &b)
{
    return a.k<b.k;
}
 
node tree[50001];
int p[50001],left[50001],right[50001],pos[50001];
 
void rotate(int i)
{
    while (p[i] && tree[i].a<tree[p[i]].a)
    {
        right[p[i]]=left[i];
        if (left[i]) p[left[i]]=p[i];
        left[i]=p[i];
        p[i]==left[p[p[i]]]?left[p[p[i]]]=i:right[p[p[i]]]=i;
        p[i]=p[p[i]];
        p[left[i]]=i;
    }
}
 
int main()
{
    right[0]=1;
    int n,rightmost(1);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        tree[i].id=i;
        scanf("%d%d",&tree[i].k,&tree[i].a);
    }
    sort(tree+1,tree+n+1);
    for (int i=1;i<=n;i++) pos[tree[i].id]=i;
    for (int i=2;i<=n;i++)
    {
        right[rightmost]=i; p[i]=rightmost; rightmost=i;
        rotate(i);
    }
    printf("YES\n");
    for (int i=1;i<=n;i++)
        printf("%d %d %d\n",tree[p[pos[i]]].id,tree[left[pos[i]]].id,tree[right[pos[i]]].id);
    return 0;
}