首页 > 2018.3.15校内互测总结-点分治-线段树

2018.3.15校内互测总结-点分治-线段树

这是曾来过咱们学校集训的一位大神出的~

T1

题目大意

给出一棵带边权的无根树,求树上前$k$大的路径的长度。

$1 leq n leq 200000$

题解

想了一上午点分治,却发现只会$O(nlog^3n)$的......

正解是二分第$k$大的权值,用点分治判断,统计路径时用两个指针扫一下权值序列就行了......

这里记录一种巧妙的,常数更小的方法。

考虑序列求前$k$大路径的经典操作:

维护一个大根堆,初始将每个左端点和它所能到达的最远右端点以及距离构成的三元组压入堆中,按照距离排序。

每次从堆顶弹出一个区间,就把当前左端点的所有右端点中,与当前弹出右端点最接近但是更劣的一个右端点作为该三元组的新右端点,重新计算距离并将三元组压回堆内,重复$k$次即可取得前$k$大路径。

现在考虑如何在树上运用这种操作:

还是考虑点分治,对于每次点分治,找出分治子树中的每个节点所属的子树,及这个点到分治重心的距离,打包成二元组存下来。

可以发现,只有子树不同的两个节点构成的路径才是有效的。因此,对于每个分治重心,将打包好的二元组按距离从大到小排序成一个序列,从后往前预处理出每个二元组向后第一个所属子树与当前二元组不同的位置。

将每个二元组与其向后第一个所属子树与当前二元组不同的位置处的二元组打包后压入一个全局堆里,作为一条路径。

这个堆的功能类似序列版本时的堆,每次弹出堆顶元素后,将这个元素左端点在对应分治重心的序列中,下一个与当前左端点所在子树不同的二元组作为新的右端点,压回堆中。

可以发现,对于每个分治重心的每个二元组,最多只有一个对应的二元组在全局堆中,空间复杂度$O(nlogn)$。同时,对于维护最外层的堆,和对二元组序列的排序两部的复杂度均为$O(nlog^2n)$,因此时间复杂度为$O(nlog^2n)$。

T2

题目大意

维护一个序列,要求支持区间最大值、区间并上一个数、区间或上一个数三种操作。

题解

出于各种原因十天前刚考过原题大家都$A$了这题~

考虑线段树。

考虑在暴力递归到底以修改每个线段树节点的基础上添加优化,那么显然有一个结论:

若一个区间某一位的值全部相同,那么对于这一位来说,接下来进行任何的修改操作,都只需要区间打$tag$,而不用暴力递归到底。

具体实现时,只需要维护一下区间或和和区间并和,并判断一下是否全为$0$或$1$即可。

然后,考虑位运算的结合律:

$(A&B|C)&D=(A&(B&D))|(C&D)$

$(A&B|C)|D=(A&B)|(C|D)$

于是维护区间或标记和区间并标记,下传时强制先做并再做或。

此时,只要在区间或时,判断一下修改的值有$1$的位置在当前区间上是否全为$0$或$1$,如果为真则打标记停止递归。区间并同理。

复杂度可通过势能分析证得是$O(nklogn)$。

T3

这是个暂时不会待填的坑~

转载于:https://www.cnblogs.com/zltttt/p/8577253.html

更多相关: