若我们并肩作战,你要照顾好自己哦?|

xingyu_xuan

园龄:1年6个月粉丝:12关注:1

笛卡尔树

附近都是喜欢花自己时间耗别人时间和心态,不想宽容别人占用自己时间的人。这些人全是傻逼,所以我开始写这个了。

这里先喷一下。

傻逼啊!我造题,我一句话没说,草泥马的一直叫,叫的还贼难听。日了狗了我真的听够了,就好像你他妈是我上司,我就是下属,就该造题。傻逼才会这么想吧!日马口气极为臭,请搞清楚是我们在无偿做事!至少我不怕说损坏了什么个人名誉甚至是集体名誉。这个集体几乎没帮过我什么,除了挂题,不过那都是小事,我维护 oj 的时间比这个多多了。你们天天都是有事有事,反而是我天天给别人讲题!最弱的给别人讲题,你想想!!!搞明白一点吧!!!

我不管了,这个集训我已经几乎受够了。出 3 次外卖事故的是我,造题最后题没了的也是我,比大多数人做出来连考题少的人也是我!!!我自己揽的活我承认,但是你就别用这种傻逼口气叫,讲题也只讲个状态。就好像什么理都在你们那边。

反正我不想收场了,这个事情不值得我素质高。

好了该补这个知识点了。

笛卡尔树是一个结构描述二元组 (ai,bi) 构成的序列。使得对于 ai,这棵树是用二叉搜索树来描述的;且对于 bi,这棵树是按照堆来描述的。

常用的就是对于一个排列去建出大根笛卡尔树或者小根笛卡尔树。此时二元组就是 (i,ai)

我们容易得出笛卡尔树的 O(nlogn) 建法,就是直接建一个 ST 表,然后递归处理。

但是实际上我们有简单的 O(n) 建法。以下是以建小根笛卡尔树为例。

考虑顺序建树,每次加入一个数 ai,记录 i1 位置的数目前的位置。因为下一个数需要满足二叉搜索树的限制,所以我们找到到根链上第一个小于 ai 的数,我们就一定把这个数放到它的右子树上。如果这个右子树已经存在,那就放到目前加入的点的左子树上。

显然发现这样加入那么记录的位置到根链就全部是右儿子。需要特判根都比目前的数大。

显然暴力跳是对的,因为相当于 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] 树的序

众所周知,二叉查找树的形态和键值的插入顺序密切相关。准确的讲:

  1. 空树中加入一个键值 k,则变为只有一个结点的二叉查找树,此结点的键值即为 k
  2. 在非空树中插入一个键值 k,若 k 小于其根的键值,则在其左子树中插入 k,否则在其右子树中插入 k

我们将一棵二叉查找树的键值插入序列称为树的生成序列,现给出一个生成序列,求与其生成同样二叉查找树的所有生成序列中字典序最小的那个,其中,字典序关系是指对两个长度同为 n 的生成序列,先比较第一个插入键值,再比较第二个,依此类推。

1n105

观察它上面定义的二叉查找树,发现生成序列的置换的小根笛卡尔树即为它生成的树。

要求最小的生成序列,相当于我们贪心的去想每次第一个数最小。那么中左右遍历一遍二叉树输出下标即可。

P9607 [CERC2019] Be Geeks!

音乐乐队 Be Geeks! 的名字并非偶然,因为所有成员都是真正的数学怪才。除此之外,他们喜欢研究数列的各种性质。下面是他们感兴趣的一个例子:

  • A 是一个非空正整数序列,A=(a1,a2,,aN)
  • G(i,j)=gcd(ai,ai+1,,aj),其中 1ijN
  • M(i,j)=max(ai,ai+1,,aj),其中 1ijN
  • P(i,j)=G(i,j)×M(i,j),其中 1ijN
  • F(A)=P(i,j)[1ijN]

给出一个序列 A,你需要求出 F(A)mod1000000007 的值。

1N2×105

笛卡尔树是可以把树论的一些东西套上去的。

笛卡尔树分治。每次考虑跨过 max 值的区间。则 M(i,j) 的部分是固定的。现在只需要考虑这部分的 gcd 之和。但是注意到 gcd 只会变化 O(logV) 次,所以可以先枚举小的那边,然后通过 O(logV) 次二分得到大的那边的分割点。注意这样的话我们需要一个 ST 表求区间 gcd,这样是 3log 的。

根据经典结论,把二分换成倍增,复杂度就均摊掉 1 个 log。

枚举小的那段的复杂度对是因为这个相当于启发式合并。

P10919 运输规划

大家都喜欢。

n 个城市,对于任意 1<in 满足第 i 个城市与第 i1 个城市间有一条双向的道路,每个城市有一个对卡车高度的限制 hi,代表只有高度小于等于 hi 的卡车可以从这个城市经过,现在有 m 个城市 S1,S2,...,Sm 各有恰好一个运输任务,任务要求编号为 i 且高度为 hSi卡车从城市 Si 出发到达任意一个有机场的城市,而有 m 个城市有机场,分别为 T1,T2,...,Tm,对于一个合法的运输方案而言,需要保证每个卡车都到达一个机场且每个机场恰好有一辆卡车抵达。一个机场可以同时被多辆卡车经过。请注意,如果你无法经过某个城市,那么你也无法抵达这个城市。

ci 表示抵达位于城市 Ti 的机场的的卡车编号,令数组 F={c1,c2,...,cm},请你最小化 F 的字典序并输出 F

我们定义两个长度为 len 的数组 A,B 满足 A 的字典序小于 B 当且仅当存在 0i<len 满足对于任意 1ji 满足 Aj=BjAi+1<Bi+1

数据保证有解,保证所有 hi 互不相同,所有 Ti 互不相同,所有 Si 互不相同。但是可能会存在 i,j 满足 Si=Tj

1n,m2×105

Hall 定理直接套上。注意读题,我们要求的是 T 匹配什么 S 的序列对应的字典序最小排列。考虑 Hall 定理的 |S|N(S),所以我们可以非常容易想到维护对于 SN(S)|S| 最小值。这个最小值一定等于 0。注意到 S|S| 和其对应的 N(S) 都可以在笛卡尔树上用一个子树描述,所以其实我们就是相当于维护每个子树的 N(S)|S|。去掉一个 S 就是 |S| 减,去掉 T 就是 N(S) 减。

这个时候已经非常明了了,我们相当于对于一个 T 找到上面最近的 0 点,这个点以上的 S 一定不合法。在这个路径上找到一个最小的 S 即可。

可以用树剖 + 线段树 + set 维护。

有一个几乎一样的题。不过是 BOJ 的。那个题就是点连排列的一个区间,扫描线类似分析即可。不过这不在笛卡尔树范畴之内。

P9152 待黑白分明

要趁黎明还

没有到来之前

无数的命运线伏延

谁与谁的轨迹交叠

不用倾注太多语言

行动回应眼前一切

握住手中仅存岁月

尝试剥去内心假面

成为彼此生命中最独特最珍贵那页

待到黑白已然分明......

以上不是题面。

Shiro 所在的城市可以看成数轴上 n 个坐标连续的点,其中 i 号点的高度为 pi,保证 p 是一个 {1,2,,n} 的排列。

3202 年的科技非常发达,发展出了虫洞列车技术。共有 n 种列车,第 i 种列车会经过所有高度大于等于 i 的位置,每种列车线路都是双向的,也就是说可以乘列车从左到右,也可以从右到左。

Shiro 想在城市里转转,她定义一个位置集合 S 合法,当且仅当我们将 S 中的位置按照高度排序后,相邻的城市可以通过乘坐一种列车在中途不停靠的情况下直达。

她会给你 q 次询问,每次给定 l,r,你需要告诉 Shiro 所有位置的高度均在 [l,r] 内的合法集合 T 的数量对 998244353 取模的结果。

1n,q2×105

最后一题。

后面的 DS 部分就简单点讲。这题卡时间和空间,素质极差。

这个题面非常让人摸不着头脑。不过如果你读懂了的话,就可以开始分讨什么位置能到达这个位置。

如果这个题你想到了笛卡尔树,那你就很牛了。作者是知道这是笛卡尔树的情况下做的,不过遇到这种排列跟值全部小于大于某个值或者直接摆明是 max 或者 min 的就可以往这边想了。

限制很史对吧?化成我们知道的东西!那么一点到另外一点一定满足什么?假设是 pq,其实容易写出:

min(ap,aq)imaxj=p+1q1(aj)i

因为只需要让一个 i 满足即可,所以相当于限制变为:

min(ap,aq)>maxj=p+1q1(aj)

这个时候其实笛卡尔树已经比较明了了,但是还是很难看出来。建出大根笛卡尔树。一个点能被其左儿子的右儿子链和右儿子的左儿子链转移。如果是单组询问那就直接 DP 就做完了。

然后呢?值域 n1 扫描线,每次加入一个点。因为大的不会影响小的的 DP 值,所以我们相当于求值 r 的所有 DP 值和。

树上随机撒点分块,每次两种转移,暴力跳父亲转移到父亲上,然后逐块处理其根对其他所有点的贡献,在值域上做一个前缀和算出贡献系数即可。

需要一些卡常。

本文作者:xingyu_xuan

本文链接:https://www.cnblogs.com/xingyuxuan/p/18571020

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   xingyu_xuan  阅读(21)  评论(0编辑  收藏  举报
(评论功能已被禁用)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起