笛卡尔树~cartesian-tree
笛卡尔树
简介
笛卡尔树是一种平衡树,它的结构和treap相同,但是由于它能在O(n)时间构造,同时具有一些很有意思的性质。
构造
笛卡尔树的节点由键值对 组成。其中键值 满足二叉搜索树性质,而键值 满足小根堆性质。
一个有趣的事实是,如果笛卡尔树的 键值确定,且 互不相同 , 互不相同,那么这个笛卡尔树的结构是唯一的。
构建笛卡尔树需要用到栈,其步骤如下:
1.首先,将节点按照 值排序,保证树具有BST性质;
2.然后一个个插入,由于BST性质,我们只能插入到右链的末端;
3.将我们插入的节点 向上跳,直到找到一个w比它小的节点,然后使成为的右儿子;
4.对于本身的右子树,则成为的左子树,由于的右儿子的一定大于因此堆性质得以保证;
我们的栈维护的是笛卡尔树的右链,由于每个元素在栈中停留的时间是一个连续的区间,所以可以用栈维护,而这个栈显然在的视角都是单调的。
1.单纯实现一个最小堆,我们使用栈作为辅助空间来构造堆.当一个数组元素要构造节点时,他可以成为右链元素的子节点(比右链元素大),或者成为右链元素的父节点(比栈顶元素小)
2.还要将最小堆变成二叉搜索树。一个已入栈元素的下标<后入栈元素的下标,所以在第一步中,变成子节点的元素必须要成为右节点;变成父节点的元素,栈中的元素必须成为他的左子树,这样才保持了基于下标的二叉搜索树性质——
代码实现:
for(int i=1;i<=n;i++){
int k=top;
while(k>0&&w[s[k]]>w[i])k--;
if(k)r[s[k]]=i;//如果u是堆顶,那就不需要成为任何壬的儿砸
if(k<top)l[i]=s[k+1];//如果刚开始爬就被截停了,那他也没有儿砸了
s[++k]=i;
top=k;
}
以点 u 为根的子树是一段连续极长区间, 是区间的最小值,区间在保证最小值不变的情况下不能再向两边延长。区间 [a,b]的最小值为
例题
Removing Blocks
有 块砖块排列成一行,从左到右编号为 到 。每一个砖块都有一个重量,砖块 的重量为 。 Snuke 会对这些 个砖块执行如下操作:
- 选择一个还没有被移除的砖块,然后移除它。这个操作的代价是与被移除的砖块相邻的砖块(包括它自己)的重量之和。我们定义两块砖 和 是相邻的,当且仅当对于所有 ,砖块 仍然没有被移除。
有 种移除砖块的可能顺序。你需要对于所有可能的顺序计算出移除完所有 块砖块的代价,并计算这些代价的和。由于答案可能非常大,答案需要对 取模。
考虑以构建键值对,以删除时间为 ,设点权为.
则一个元素子树内的权值和就是删去它的贡献,设每个元素深为,则有总代价为
再考虑对于一个 , 是它祖先的充分必要条件是 在之前被删去,这个事件的期望是
所以为
所以答案为
由于中间那一坨可以预处理,所以总时间复杂度O(n);
代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,mod=1e9+7;
int fac[N],inv[N],s[N];
int n,a[N],ans;
signed main(){
cin>>n;
fac[0]=fac[1]=1;inv[0]=inv[1]=1;
for(int i=2;i<=100000;i++)fac[i]=fac[i-1]*i%mod;
for(int i=2;i<=100000;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;//线性求逆元
for(int i=1;i<=100000;i++)s[i]=(s[i-1]+inv[i])%mod;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)ans=(ans+(s[i]+s[n-i+1]-1)*a[i])%mod;
cout<<ans*fac[n]%mod;
}
模意义下的逆元和实数意义下的逆元是同余的——hzm
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现