题目链接:点击查看
题目大意:给出n个节点的key和val,构造出其笛卡尔树的原型
笛卡尔树的定义:
所谓笛卡尔树,就是将给定的n个二元组(key,val)建成一棵树。使得:
- 如果只关注key,那么这是一棵二叉搜索树
- 如果只关注val,那么这是一个堆
- 对于任意三个节点fa,ls,rs,满足:
- key[ls]<key[fa]<key[rs]
- val[fa]>=max(val[ls],val[rs])
题目分析:首先我们需要先知道怎么建立笛卡尔树,我们可以按照key进行排序,经过排序之后,在最右边的树链上操作,对于每一个节点的val寻找一下该节点需要插入的位置,需要满足的条件是当前节点插入后的val值满足条件比其上面的节点要大,比起下面的节点要小,找到合适的位置后,将原本的链断开,将x插入,并将原本x之下的那条链连接到x的左子树上,这样我们就成功插入一个节点x了
先说一下为什么要这样插点,因为我们需要满足二叉搜索树的条件并且已经提前按照key值非降排好序了,所以在插入节点x之前,树中的所有key值都比节点x的key值要小,若我们只关注key的话,那么直接把点x插入到主根root节点向右遍历最下面的那个节点的右儿子即可,也就是最右端树链的末端,因为同时需要满足对于val值建立小顶堆的条件,所以我们需要在最右端的树链上寻找一个合适的位置插入节点x,当找到合适的位置后,这个位置肯定是满足了对于val值建堆的条件了,但却不满足对于key建二叉搜索树的条件了,所以我们需要稍微操作一下,因为当前x的key值在整棵树中是最大的,所以对于其父节点以及其祖先节点,肯定在右链的末端是合适的位置,但对于节点x下面的树链,也就是原本这个位置右链的下半部分来说,我们需要将这下半部分转移到当前节点x的左链上,这样就可以同时满足上述两个条件了
可能看起来比较难理解,可以自己动手画一下上面的这个过程,我也是稍微画了画图之后就豁然开朗了
现在说一下实现方法,对于key排序没什么好说的,现在就是对于每个点x需要找到点x插入的位置,这个位置肯定是从右链最下面开始,找到小于等于点x的第一个节点作为点x的父节点,我们该怎么快速找到这个父节点呢
网上的正解都是清一色的用单调栈来维护,确实,因为我们只需要在右链上寻找合适的位置,而且整棵树对于val满足堆的性质,所以从根部沿着右链到达叶子结点,这一整条树链实际上就是一个非严格递减的序列,这样一来我们就可以直接用单调栈来维护了,每次维护完之后栈顶就是我们需要寻找的节点编号了,注意,单调栈里维护的为每个节点的下标,也就是id,通过这个id就可以直接访问其内部成员了,用单调栈的话因为每个成员至多访问一次,所以时间复杂度也就是O(n)建树了
不过zx学长想出了另一个方法,也就是根据建树的整个过程得到启发的,因为每次我们找到一个放置点x的位置后,整个树链的下半部分都会拐个弯,拐到节点x的左节点去了,所以此时最右边的树链相当于将原本下半部分的节点删除掉了,然后此时的点x一定是最右端树链的最末端的节点,此时我们每次可以沿着最末端的节点往上跳,跳完之后直接将下半部分的树链拐弯,这样理论上每个节点至多也是遍历一次,时间复杂度是O(n)
其实两种方法大同小异,都离不开笛卡尔树的两个关键性质,理解了如何根据其性质建树之后就想怎么建就怎么建了。。
代码:
单调栈:
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=5e4+100;
struct Node
{
int l,r,fa,val,key,id;
bool operator<(const Node& a)const
{
return key<a.key;
}
}tree[N],ans[N];
stack<int>s;//维护id,维护val的小顶栈
void insert(int x)
{
while(s.size()&&tree[s.top()].val>tree[x].val)//单调栈维护非严格递增序列
s.pop();
tree[x].l=tree[s.top()].r;//更新 节点x 的左儿子
tree[tree[s.top()].r].fa=x;//更新 节点x 的左儿子 的父节点
tree[x].fa=s.top();//更新 节点x 的父节点
tree[s.top()].r=x;//更新 节点x 的父节点 的右儿子
s.push(x);
}
void init()
{
tree[0].val=-inf;
tree[0].l=tree[0].r=0;
s.push(0);
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
int n;
while(scanf("%d",&n)!=EOF)
{
init();
for(int i=1;i<=n;i++)
{
scanf("%d%d",&tree[i].key,&tree[i].val);
tree[i].id=i;
}
sort(tree+1,tree+1+n);
for(int i=1;i<=n;i++)
insert(i);
for(int i=1;i<=n;i++)
{
ans[tree[i].id].fa=tree[tree[i].fa].id;
ans[tree[i].id].l=tree[tree[i].l].id;
ans[tree[i].id].r=tree[tree[i].r].id;
}
printf("YES\n");
for(int i=1;i<=n;i++)
printf("%d %d %d\n",ans[i].fa,ans[i].l,ans[i].r);
}
return 0;
}
暴跳父亲:
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=5e4+100;
struct Node
{
int l,r,fa,val,key,id;
bool operator<(const Node& a)const
{
return key<a.key;
}
}tree[N],ans[N];
void insert(int pos)
{
int cur=pos-1;
while(tree[cur].val>tree[pos].val)//用while暴跳父亲,直到父亲节点的val小于等于当前节点的val
cur=tree[cur].fa;
tree[tree[cur].r].fa=pos;//更新 节点x 的左儿子 的父节点
tree[pos].l=tree[cur].r;//更新 节点x 的左儿子
tree[cur].r=pos;//更新 节点x 的父节点 的右儿子
tree[pos].fa=cur;//更新节点x 的父节点
}
void dfs(int x)
{
if(!x)
return;
ans[tree[x].id].fa=tree[tree[x].fa].id;
ans[tree[x].id].l=tree[tree[x].l].id;
ans[tree[x].id].r=tree[tree[x].r].id;
dfs(tree[x].l);
dfs(tree[x].r);
}
void init()
{
tree[0].val=-inf;
tree[0].l=tree[0].r=0;
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
int n;
while(scanf("%d",&n)!=EOF)
{
init();
for(int i=1;i<=n;i++)
{
scanf("%d%d",&tree[i].key,&tree[i].val);
tree[i].id=i;
}
sort(tree+1,tree+1+n);
for(int i=1;i<=n;i++)
insert(i);
dfs(tree[0].r);
printf("YES\n");
for(int i=1;i<=n;i++)
printf("%d %d %d\n",ans[i].fa,ans[i].l,ans[i].r);
}
return 0;
}
来源:CSDN
作者:Frozen_Guardian
链接:https://blog.csdn.net/qq_45458915/article/details/103236519