首页 > 其他分享 >笛卡尔树~cartesian-tree

笛卡尔树~cartesian-tree

时间:2023-03-10 20:46:44浏览次数:43  
标签:cartesian 笛卡尔 int sum 移除 tree 键值 砖块

笛卡尔树

简介

笛卡尔树是一种平衡树,它的结构和treap相同,但是由于它能在O(n)时间构造,同时具有一些很有意思的性质。

构造

笛卡尔树的节点由键值对 \((k,w)\) 组成。其中键值 \(k\) 满足二叉搜索树性质,而键值 \(w\) 满足小根堆性质。

一个有趣的事实是,如果笛卡尔树的 \((k,w)\) 键值确定,且 \(k\) 互不相同 ,\(w\) 互不相同,那么这个笛卡尔树的结构是唯一的。

构建笛卡尔树需要用到栈,其步骤如下:

1.首先,将节点按照 \(k\) 值排序,保证树具有BST性质;

2.然后一个个插入,由于BST性质,我们只能插入到右链的末端;

3.将我们插入的节点 \(u\) 向上跳,直到找到一个w比它小的节点\(v\),然后使\(u\)成为\(v\)的右儿子;

4.对于\(v\)本身的右子树,则成为\(u\)的左子树,由于\(v\)的右儿子的\(w\)一定大于\(w_u\)因此堆性质得以保证;

我们的栈维护的是笛卡尔树的右链,由于每个元素在栈中停留的时间是一个连续的区间,所以可以用栈维护,而这个栈显然在\((k,w)\)的视角都是单调的。

1.单纯实现一个最小堆,我们使用栈作为辅助空间来构造堆.当一个数组元素要构造节点时,他可以成为右链元素的子节点(比右链元素大),或者成为右链元素的父节点(比栈顶元素小)

2.还要将最小堆变成二叉搜索树。一个已入栈元素的下标<后入栈元素的下标,所以在第一步中,变成子节点的元素必须要成为右节点;变成父节点的元素,栈中的元素必须成为他的左子树,这样才保持了基于下标的二叉搜索树性质——\(votrubac\)

代码实现:

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 为根的子树是一段连续极长区间,\(w_u\) 是区间的最小值,区间在保证最小值不变的情况下不能再向两边延长。区间 [a,b]的最小值为 \(w[lca(a,b)]\)

例题

Removing Blocks

有 \(N\) 块砖块排列成一行,从左到右编号为 \(1\) 到 \(N\) 。每一个砖块都有一个重量,砖块 \(i\) 的重量为 \(A_i\)。 Snuke 会对这些 \(N\) 个砖块执行如下操作:

  • 选择一个还没有被移除的砖块,然后移除它。这个操作的代价是与被移除的砖块相邻的砖块(包括它自己)的重量之和。我们定义两块砖 \(x\) 和 \(y ~(x \leq y)\) 是相邻的,当且仅当对于所有 \(z (x \leq z \leq y)\) ,砖块 \(z\) 仍然没有被移除。

有 \(N!\) 种移除砖块的可能顺序。你需要对于所有可能的顺序计算出移除完所有 \(N\) 块砖块的代价,并计算这些代价的和。由于答案可能非常大,答案需要对 \(10^9+7\) 取模。

考虑以\((数组下标,删除时间)\)构建键值对,以删除时间为 \(w\) ,设点权为\(a_i\).

则一个元素子树内的权值和就是删去它的贡献,设每个元素深为\(h_i\),则有总代价为

\[\sum_{i=1}^{n}h_i\times a_i \]

再考虑对于一个 \(j\) , \(i\) 是它祖先的充分必要条件是 \(j⋯i-1\)在\(i\)之前被删去,这个事件的期望是

\[\frac{(i-j)!}{(i-j+1)}= \frac1{i-j+1} \]

所以\(E(h_i)\)为

\[\sum_{x=1}^{i-1}\frac{1}{i-x+1}+\sum_{i-1}^{n}\frac{1}{x-i+1}+1 \]

所以答案为

\[n!\times\large\sum_{i=1}^{n}( \sum_{x=1}^{i-1}\frac{1}{i-x+1}+\sum_{i-1}^{n}\frac{1}{x-i+1}+1\large)\times a_i \]

由于中间那一坨可以预处理,所以总时间复杂度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

标签:cartesian,笛卡尔,int,sum,移除,tree,键值,砖块
From: https://www.cnblogs.com/Hushizhi/p/17204602.html

相关文章

  • Tree-shaking(摇树优化)
    Tree-Shaking(摇树优化):主要用于前端的性能优化,即在导入模块的时候,将一些项目用不到的代码像树木落叶一样摇掉,且tree-shaking只支持ESModule,不支持Common.jsTree-Shaki......
  • EasyUI的combotree 默认节点选中呢
    $('#selShenqFuwujg').combotree({url:'../../GetFuwujgInfo.aspx?type=GetFuwujgTree&PID=',onLoadSuccess:function(node,data){......
  • SourceTree安装部署
    SourceTree1、下载地址https://www.sourcetreeapp.com/注意:使用sourcetree必须要先安装git客户端才可以git客户端的下载地址https://git-scm.com/downloads2、git......
  • sourcetree提示ssh密钥认证失败
    今天使用sourcetree推送,麻烦来了,推送不了  解决方法:修改SSH客户端配置【工具】-【选项】-【一般】,将默认的SSH客户端-PuTTY/Plink改为OpenSSH,把它选择为OpenSSHSS......
  • CF207C3 Game with Two Trees 题解
    脑子不够,科技来凑。不过好像也没有用多么高级的科技……首先这个题目很坏,它让你翻转\(S_{t_2}\)。即从\(t_2\)某个节点往下走到另一个节点的路径所表示的字符串。这个......
  • 106. Construct Binary Tree from Inorder and Postorder Traversal
    题目Giveninorderandpostordertraversalofatree,constructthebinarytree.Note:Youmayassumethatduplicatesdonotexistinthetree.思路本题......
  • 动态树(Link Cut Tree)
    动态树(LinkCutTree)简介Link/CutTree是一种数据结构,我们用它来解决动态树问题。Link/CutTree又称\(Link-CutTree\),简称\(LCT\),但它不叫动态树,动态树是指一类问......
  • KDTree实现KNN算法
    KDTree实现KNN算法完整的实验代码在我的github上......
  • F. Timofey and Black-White Tree 2100 (树 根号分治 思维)
    https://codeforces.com/problemset/problem/1790/F题意:给定一棵树,需要将其染为全黑,初始时只有一个点为黑色,给定一个序列c,按招顺序染色,要求每次染色后给出当前任意两黑点......
  • 【学习笔记】dsu on tree
    看到了就来学一下。思想借鉴了一类启发式合并的思想?由于树的分叉结构有可以二分的性质,有重儿子的信息是可以直接从子树继承,轻儿子不超过\(log\)层。于是先计算轻儿子,......