首页 > 其他分享 >『线段树合并』Day7

『线段树合并』Day7

时间:2024-08-15 11:50:13浏览次数:14  
标签:int Day7 线段 合并 mid v1 序列 sum

颓了一天了。md

虽然还没有过线段树合并板题,但早就用过了。

注意,单次合并复杂度是 \(O(n\log n)\) 的,但是一直合并,保证最终共有 \(n\) 个元素的话,总时间复杂度也是 \(o(n\log n)\) 的。不要理解成单次 \(\log n\)。

一个人千万不能忘记自己的初心,有时候需要静下心来想一想自己到底应该做什么。

难道之前逼自己赶上那么多的努力真的要白费吗。

习题

A 更为厉害

差不多 30 min 秒了。

考虑两种情况,\(b\) 是 \(a\) 的祖先,那么 \(c\) 只能是 \(a\) 的子树。

第二种情况 \(b\) 在 \(a\) 的子树里,但是距离有 \(k\) 的限制。然后 \(c\) 是 \(b\) 的子树,也就是 \(siz_b-1\) 种 \(c\)。

考虑 \(b\) 的个数,按照深度为下标建主席树,每次查询深度区间里在 \(a\) 子树的 dfn 区间中的 sum 即可。

B Minimax

之前看过线段树合并优化 dp,第一次实现。

由于是二叉树,所以 dp 的形式非常简单,就是前缀和后缀和加上一坨。

dp 是二维的,直接把第二维放进 ds 下标,前后缀和可以在你合并左右儿子的时候直接由 sum 得到。

可以结合代码理解。

int merge(int p, int q, int v1, int v2, int w, int l, int r) {
	if(!p and !q) return 0;
	v1 %= Mod, v2 %= Mod;
	if(!p) return upd(q, v2), q;
	if(!q) return upd(p, v1), p;
	pushdown(p); pushdown(q); 
	
	int mid = l + r >> 1;
	int s1 = T[ls].sum, s2 = T[rs].sum, s3 = T[T[q].l].sum, s4 = T[T[q].r].sum;
	
	ls = merge(ls, T[q].l, v1 + (1 - w) * s4, v2 + (1 - w) * s2, w, l, mid);
	rs = merge(rs, T[q].r, v1 + w * s3, v2 + w * s1, w, mid + 1, r);
	
	return pushup(p), p; 
}

C 众数

4 操作就是线段树合并板。考虑众数操作。

有一种好像是众数的套路做法,就是你维护当前的 house 数,如果新加入的数相同那么 cnt ++,否则 cnt --。如果 cnt < 0 那么替换 house 为 nowx。

容易发现序列加入顺序不会影响最终答案的取值。用 ds 维护这个过程。

但是没有必要啊。你众数在的那个值域区间一定满足 Sum > len / 2,根据这个直接 ds 二分即可。注意是 \(m\) 棵一起二分。

数据造得好。

D 旋转树木 Tree Rotations

一眼知交换左右儿子不会影响父亲的集合。

考虑合并的时候内部不可能有新的贡献,你只会算当前右子树中比左子树 val 大的、小的有多少个。

所以只要贪心选更小的一种方案即可。

在你合并的时候,由于要分到左右去 merge,所以你可以直接得出左右儿子的 sum,计算以 mid 作为分界的逆序对。然后递归到内部再算。

代码和 B 差不多。

E 排序

典。

要对数地对一般序列排序是脑子有问题才会想优化的。

但是题目非要我们排序,考虑简单方法。

如果是对 01 序列排序,就可以只维护 1 的个数,然后前缀后缀赋值操作即可。

而你询问只有一个单点,所以我们可以二分这个点是多大,根据所有数和二分值的大小关系转化成 01 序列。

为什么是对的啊。

排序后的序列一定是不变的,位置 q 也不变,只是你二分的 01 序列在变,而且 0 越来越多。(随着 mid 的增大)

什么时候 q 变成 0,就可以了。

晚测 JOISC2017 Sparkles

nb 题,双序列原 & 加强。

4 月做了双序列,但为什么这道题没做出来呢。

因为一开始就想错了。

想这种题怎么可能贪心,绅士居然想出了从 \(S\) 集合里面分离 \(B\) 集合出去的zhizhang做法,复杂度指数线性对数。可过。

注意:一个人肯定是等火要烧完了才传递,以及路径上经过的人会跟着这个人走,不劣。

所以推出:每个时刻,和 \(k\) 在一起的人构成一个连续的区间 \([l,r]\)

你可以用一个平方 dp 进行转移。

首先,火把在最优传递方法下,是可以一共有 \((r-l)\)

标签:int,Day7,线段,合并,mid,v1,序列,sum
From: https://www.cnblogs.com/LCat90/p/18360608

相关文章

  • python之numpy(4 选择数据及合并)
    选择数据importnumpyasnpm=np.random.random((3,3))print(m)print(m[0],m[1][1],sep='\n')print(m[1,1])print(m[1,:])print(m[:,1])结果:[[0.25960570.047399260.76332494][0.865032270.290489970.79591841][0.50535280.201822340.19601046]][......
  • 线段树杂谈
    动态开点线段树引入普通的线段树写法有一个显然的缺点——空间。堆式存贮使得我们开线段树时需要用$4n$的空间。冗余空间高达$2n$。而且,在大多数情况下线段树中并不是每个节点都会被用到,这时我们就可以使用动态开点线段树,不仅所用的空间小,实现起来的代码也比普通线段树......
  • 线段树+懒标记 (区间修改,区间查询)
    原作者:董晓P3372【模板】线段树1//结构体版#include<cstring>#include<iostream>#include<algorithm>usingnamespacestd;#defineN100005#defineLLlonglong#definelcu<<1#definercu<<1|1LLw[N];structTree{//线段树LLl,r,......
  • OpenCV图像处理——直线拟合并找出拟合直线的起点与端点
    引言对轮廓进行分析,除了可以对轮廓进行椭圆或者圆的拟合之外,还可以对轮廓点集进行直线拟合。在OpenCV中,直线拟合通常是通过cv::fitLine函数实现的,该函数采用最小二乘法对一组2D或3D点进行直线拟合。对于2D点集,拟合结果是一个cv::Vec4f类型的向量,包含了直线的方......
  • 李超线段树
    用途:用于二维坐标系维护多条线段。算法:本质上是采用标记永久化,对每个线段树节点维护一个标记表示该区间存在这一条线段,查询时从上到下经过节点的标记即为该横坐标上可能经过的线段。下面需在标记(线段)间的比较上作考虑:建议画图理解此时对于一个区间\([l,r]\),找出中点\(mid......
  • unity中, 二维平面上,求从点A出发,沿着方向B,与线段C的交点
    代码说明:点A:起始点。方向B:一个方向向量,表示从点A出发的方向。线段C:由两个点C1和C2定义。1usingUnityEngine;23publicclassLineIntersection:MonoBehaviour4{5//返回从点A出发,沿着方向B,与线段C的交点。如果没有交点,则返回null6publicstati......
  • 合并两个有序链表
    1、publicclassMergeTwoSortedLists{//方法:合并两个有序链表publicstaticListNodemergeTwoLists(ListNodel1,ListNodel2){//创建一个虚拟节点作为合并后链表的头节点ListNodedummy=newListNode(0);ListNodecurrent=du......
  • 可持久化线段————主席树(洛谷p3834)
    洛谷P3834可持久化线段树2问题描述:给定n各整数构成的序列,求指定区间[L,R]内的第k小值(求升序排序后从左往右数第k个整数的数值)输入:第一行输入两个整数n,m,分别代表序列长度n和对序列的m次查询;第二行输入n个整数,表示序列的n个整数;之后的m行,每行输入3个整数L,R,k,表示查询......
  • 线段树
    模板记得开4倍空间!!!Code#include<bits/stdc++.h>#definelllonglong#definepfprintf#definesfscanfusingnamespacestd;constintN=1e5+7;inttr[N*4];intf[N*4];inta[N];intn,q;intl,r,val;voidbuild(intu,intl,intr){ if(l==r){ tr[u]=a......
  • 【笔记】吉如一线段树
    【笔记】吉如一线段树吉如一论文(CQBZ内网,在PDF的103页1区间最值操作1.1区间取min(max),区间和当前应该修改值为\(x\);维护区间最大值\(mx\),最大值个数\(t\),严格次大值\(se\)。如果走到一个区间上,如果:\(x\gemx\),说明取min操作没用,直接return;\(mx>x>se\),打标......