笛卡尔树
附近都是喜欢花自己时间耗别人时间和心态,不想宽容别人占用自己时间的人。这些人全是傻逼,所以我开始写这个了。
这里先喷一下。
傻逼啊!我造题,我一句话没说,草泥马的一直叫,叫的还贼难听。日了狗了我真的听够了,就好像你他妈是我上司,我就是下属,就该造题。傻逼才会这么想吧!日马口气极为臭,请搞清楚是我们在无偿做事!至少我不怕说损坏了什么个人名誉甚至是集体名誉。这个集体几乎没帮过我什么,除了挂题,不过那都是小事,我维护 oj 的时间比这个多多了。你们天天都是有事有事,反而是我天天给别人讲题!最弱的给别人讲题,你想想!!!搞明白一点吧!!!
我不管了,这个集训我已经几乎受够了。出 3 次外卖事故的是我,造题最后题没了的也是我,比大多数人做出来连考题少的人也是我!!!我自己揽的活我承认,但是你就别用这种傻逼口气叫,讲题也只讲个状态。就好像什么理都在你们那边。
反正我不想收场了,这个事情不值得我素质高。
好了该补这个知识点了。
笛卡尔树是一个结构描述二元组 构成的序列。使得对于 ,这棵树是用二叉搜索树来描述的;且对于 ,这棵树是按照堆来描述的。
常用的就是对于一个排列去建出大根笛卡尔树或者小根笛卡尔树。此时二元组就是 。
我们容易得出笛卡尔树的 建法,就是直接建一个 ST 表,然后递归处理。
但是实际上我们有简单的 建法。以下是以建小根笛卡尔树为例。
考虑顺序建树,每次加入一个数 ,记录 位置的数目前的位置。因为下一个数需要满足二叉搜索树的限制,所以我们找到到根链上第一个小于 的数,我们就一定把这个数放到它的右子树上。如果这个右子树已经存在,那就放到目前加入的点的左子树上。
显然发现这样加入那么记录的位置到根链就全部是右儿子。需要特判根都比目前的数大。
显然暴力跳是对的,因为相当于 DFS 了一遍整个树。代码是好写的。
代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e7+5;
int n,a[M];
unsigned long long ans1,ans2;
int fa[M],L[M],R[M],rt,now;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
now=1;
for(int i=2;i<=n;i++){
while(fa[now]&&a[i]<a[fa[now]])now=fa[now];
if(a[now]<a[i]){
fa[i]=now;R[now]=i;
}else{
fa[i]=fa[now],R[fa[now]]=i,fa[now]=i;
L[i]=now;
}
now=i;
}
for(int i=1;i<=n;i++)ans1^=1ull*i*(L[i]+1),ans2^=1ull*i*(R[i]+1);
cout<<ans1<<" "<<ans2<<"\n";
return 0;
}
当然有些例题。
P1377 [TJOI2011] 树的序
众所周知,二叉查找树的形态和键值的插入顺序密切相关。准确的讲:
- 空树中加入一个键值 ,则变为只有一个结点的二叉查找树,此结点的键值即为 。
- 在非空树中插入一个键值 ,若 小于其根的键值,则在其左子树中插入 ,否则在其右子树中插入 。
我们将一棵二叉查找树的键值插入序列称为树的生成序列,现给出一个生成序列,求与其生成同样二叉查找树的所有生成序列中字典序最小的那个,其中,字典序关系是指对两个长度同为 的生成序列,先比较第一个插入键值,再比较第二个,依此类推。
。
观察它上面定义的二叉查找树,发现生成序列的置换的小根笛卡尔树即为它生成的树。
要求最小的生成序列,相当于我们贪心的去想每次第一个数最小。那么中左右遍历一遍二叉树输出下标即可。
P9607 [CERC2019] Be Geeks!
音乐乐队 Be Geeks! 的名字并非偶然,因为所有成员都是真正的数学怪才。除此之外,他们喜欢研究数列的各种性质。下面是他们感兴趣的一个例子:
- 设 是一个非空正整数序列,。
- ,其中 。
- ,其中 。
- ,其中 。
- 。
给出一个序列 ,你需要求出 的值。
。
笛卡尔树是可以把树论的一些东西套上去的。
笛卡尔树分治。每次考虑跨过 值的区间。则 的部分是固定的。现在只需要考虑这部分的 之和。但是注意到 只会变化 次,所以可以先枚举小的那边,然后通过 次二分得到大的那边的分割点。注意这样的话我们需要一个 ST 表求区间 ,这样是 3log 的。
根据经典结论,把二分换成倍增,复杂度就均摊掉 1 个 log。
枚举小的那段的复杂度对是因为这个相当于启发式合并。
P10919 运输规划
大家都喜欢。
有 个城市,对于任意 满足第 个城市与第 个城市间有一条双向的道路,每个城市有一个对卡车高度的限制 ,代表只有高度小于等于 的卡车可以从这个城市经过,现在有 个城市 各有恰好一个运输任务,任务要求编号为 且高度为 的卡车从城市 出发到达任意一个有机场的城市,而有 个城市有机场,分别为 ,对于一个合法的运输方案而言,需要保证每个卡车都到达一个机场且每个机场恰好有一辆卡车抵达。一个机场可以同时被多辆卡车经过。请注意,如果你无法经过某个城市,那么你也无法抵达这个城市。
记 表示抵达位于城市 的机场的的卡车编号,令数组 ,请你最小化 的字典序并输出 。
我们定义两个长度为 的数组 满足 的字典序小于 当且仅当存在 满足对于任意 满足 且 。
数据保证有解,保证所有 互不相同,所有 互不相同,所有 互不相同。但是可能会存在 满足 。
。
Hall 定理直接套上。注意读题,我们要求的是 匹配什么 的序列对应的字典序最小排列。考虑 Hall 定理的 ,所以我们可以非常容易想到维护对于 的 最小值。这个最小值一定等于 。注意到 的 和其对应的 都可以在笛卡尔树上用一个子树描述,所以其实我们就是相当于维护每个子树的 。去掉一个 就是 减,去掉 就是 减。
这个时候已经非常明了了,我们相当于对于一个 找到上面最近的 点,这个点以上的 一定不合法。在这个路径上找到一个最小的 即可。
可以用树剖 + 线段树 + set 维护。
有一个几乎一样的题。不过是 BOJ 的。那个题就是点连排列的一个区间,扫描线类似分析即可。不过这不在笛卡尔树范畴之内。
P9152 待黑白分明
要趁黎明还
没有到来之前
无数的命运线伏延
谁与谁的轨迹交叠
不用倾注太多语言
行动回应眼前一切
握住手中仅存岁月
尝试剥去内心假面
成为彼此生命中最独特最珍贵那页
待到黑白已然分明......
以上不是题面。
Shiro 所在的城市可以看成数轴上 个坐标连续的点,其中 号点的高度为 ,保证 是一个 的排列。
3202 年的科技非常发达,发展出了虫洞列车技术。共有 种列车,第 种列车会经过所有高度大于等于 的位置,每种列车线路都是双向的,也就是说可以乘列车从左到右,也可以从右到左。
Shiro 想在城市里转转,她定义一个位置集合 合法,当且仅当我们将 中的位置按照高度排序后,相邻的城市可以通过乘坐一种列车在中途不停靠的情况下直达。
她会给你 次询问,每次给定 ,你需要告诉 Shiro 所有位置的高度均在 内的合法集合 的数量对 取模的结果。
。
最后一题。
后面的 DS 部分就简单点讲。这题卡时间和空间,素质极差。
这个题面非常让人摸不着头脑。不过如果你读懂了的话,就可以开始分讨什么位置能到达这个位置。
如果这个题你想到了笛卡尔树,那你就很牛了。作者是知道这是笛卡尔树的情况下做的,不过遇到这种排列跟值全部小于大于某个值或者直接摆明是 或者 的就可以往这边想了。
限制很史对吧?化成我们知道的东西!那么一点到另外一点一定满足什么?假设是 ,其实容易写出:
因为只需要让一个 满足即可,所以相当于限制变为:
这个时候其实笛卡尔树已经比较明了了,但是还是很难看出来。建出大根笛卡尔树。一个点能被其左儿子的右儿子链和右儿子的左儿子链转移。如果是单组询问那就直接 DP 就做完了。
然后呢?值域 扫描线,每次加入一个点。因为大的不会影响小的的 DP 值,所以我们相当于求值 的所有 DP 值和。
树上随机撒点分块,每次两种转移,暴力跳父亲转移到父亲上,然后逐块处理其根对其他所有点的贡献,在值域上做一个前缀和算出贡献系数即可。
需要一些卡常。
本文作者:xingyu_xuan
本文链接:https://www.cnblogs.com/xingyuxuan/p/18571020
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步