7.15

数据结构

堆:就是priority_queue

讲完了  ------------------by dms

树上倍增qwq

 作用:求最短路(在树上)

A走到lCA+B走到LCA就是最短路

lca求法:

朴素:

求出每个节点的深度

step1:如果a的深度比b的深度小,就交换a,b(为了处理方便)

step2:把a跳到b的深度(a不停的向上跳,当a,b处于同一深度的时候停止)

step3:a,b一起向上跳,直到a=b为止

复杂度:O(深度)

我们发现这时如果树是个链的话就炸了

我们想想怎么优化

我们可以在向上跳的时候优化(也就是2,3步)

我们设p[x][i]表示x的2i的祖先节点,so p[x][i]=p[p[x][i-1]][i-1]

why?就像爷爷是爸爸的爸爸一样

step2优化:a和b会有深度差delta,我们把delta转换为二进制,如果delta的第i位是1,则a就变成p[a][i](注意这里从高位到低位循环)

step3优化:我们发现a,b到了lca之后,再往上跳就都一样了,我们很难确定lca,但是可以确定lca的儿子,即最浅的a≠b的位置。

我们循环i从16(当然也可以是20神马的)到0,如果p[a][i]≠p[b][i],则让a=p[a][i],b=p[b][i],一直向上跳

我们还可以用lca求树上任意两个点的路径

我们求出根到a的路径+根到b的路径,再减去2*根到lca的路径

其实差分问题都可以用lca来做辣(比如还有乘法什么的)

用途:查询[l,r]的区间最大值,不支持修改

mx[i][j]表示[i,i+2j-1]这个区间的最大值,可以预处理

查询:求出区间长度l,那log2l就可以把整个区间分成两段(可能会有重叠,不过木有瓜系),设j=log2l,那查询的区间就是max(mx[i][j],mx[r-(1<<j)+1][j](这里i是左端点,r是右端点)

预处理:st[i][j]=max(st[i][j-1],st[i+1<<(j-1)][j-1]

神马是哈希?就是一个函数。平常说的哈希就是将一个字符串转成一个数。

怎么整???

我们先考虑一个字符串是几进制的数(是几进制看心情咯,不过建议取一个大于字符集大小的质数)

然后暴力计算,如果字符串里面有数字肿么办?

取ASCLL码值呗。然后我们发现转着转着就爆了,所以我们要取模。可以模大质数,也可以自然溢出(unsigned longlong)

HASH有冲突怎么办?认命双重哈希,就是设计两个模数。如果还用冲突,那就3个

我们现在能够求出一个整串的哈希,但是我们想要求子串,怎么办?

我们可以求每个前缀的哈希值

举个栗子:dingmingshuohaokeai

d:d

di:d2+i

din:d3+i2+n

ding:d4+i3+n2+g

有什么用呢?

假设我们要求ing的哈希,那f(ing)=f(ding)-f(d)*p3

那化成一般的式子就是f(子串)=f(前缀)-f(不是子串的部分)*p子串长度

即hash(i,j)=hash(j)-hash(i-1)*pj-i+1

要求:快,准,尽可能避免冲突(只有神仙能做到qwq)

 

上面是路径压缩qwq

用fa[x]表示x的父亲.

合并a,b:让a的祖宗认b的祖宗为爹。

找x的祖宗:如果x的祖宗是自己,就返回x,否则返回fa[x],这里常用路径压缩进行优化,即返回时顺带把x的父亲,爷爷等等的fa都改成x的最高的祖宗

树状数组

我们接下来要种几棵线段树

线段树是用来干什么的呢?当然是等它开花结果卖钱了(雾)

线段树支持区间(单点)修改,区间(单点)查询

我们种下线段树之后第一步是什么?给树浇水(雾)

我们先来考虑又直接又暴力的单点修改

就直接修改呗,都说了很暴力辣

那区间查询呢?

我们递归找答案。如果当前节点表示的区间被要求的区间完全覆盖,就不用往下再递归了,直接返回。否则看要求的区间与当前节点的哪个儿子有交集。哪个有交集就递归哪个。

我们再来考虑区间修改。

我们发现暴力区间修改的复杂度是O(nlogn)的,比不用线段树还慢qwq

所以我们需要懒标记来加速

这样就很像我们的区间查询了。也是递归在区间上打懒标记,递归的规则与区间查询一样。

有了懒标记就要注意了,如果我们不是对节点代表的这整个区间进行操作,那就要标记下传(不管是查询还是修改)。

代码

qwqqwq

n个节点,n-1条边,说明这个图是棵树

对三个人两两求lca,会有以下三种情况(其中ABC分别代表那三个人)

 

 

我们发现一定会有两组lca是重复的,那剩下那个不同的lca就是集合地点辣

 (打表找规律得,没有证明)

qwqqwq 

维护两个堆,一个大根堆,一个小根堆。一直往大根堆里面塞,保证大根堆里面的元素少于k/2+1个。如果出现元素个数≥k/2+1,就弹掉堆顶,把堆顶塞进小根堆。这样的大根堆堆顶就是答案。

我们发现,越先被合并的那堆被算进答案的次数就越多,所以我们要合并尽可能小的两堆

贪心完毕,没有代码

据说这是哈夫曼树的模型

太难了不会qwq

 

 这里可以由低海拔的点走到高海拔的点,其中T是输入给定的

我们按照相邻两个格子的高度差建边。

那这玩意不就是第T-1短的边权吗

推导:由于T已知,而且n-1条边可以连通n个点,那么当连了T-1条边的时候,就能够到达T个点了。为了让D最小,所以我们先把所有的边按照边权(也就是高度差的绝对值)从小到大排序,然后一条一条的连边。这时D=第T-1短的边权

(树状数组模型1)

我们先想暴力怎么求逆序对数

我们可以双重循环扫描比i大且在i前面出现的的所有j,然后加一加

我们在暴力的基础上再进行一番优化

我们发现,每次枚举i都暴扫一遍真的很浪费有木有,那怎么统计i与1~i-1到底构成了多少个逆序对呢?

我们把ai的权值作为树状数组的下标,每出现一次权值为i的数,那就对树状数组进行一次操作(从c[i]开始),然后sum(a[i])就是比a[i]小的数出现的次数,那比a[i]大的数出现的次数就是i-sum(a[i])

然后我们发现空间炸了,因为a[i]实在是太大了

那我们可以离散化对吧

然后就能A了

在这里因为y是递增的,所以我们可以无视掉y的存在,然后只要求x<当前的x的所有星星就行了

我们注意到m肥肠之小

所以开m个树状数组进行操作

每个树状数组的c[i]记录下标为i的序列中的数%m后是否等于当前这个树状数组的编号

如果要改变,就直接改,如果要查询,就在第mod个树状数组里面查询,sum(r)-sum(l-1)就是答案

 一:平衡树(set,splay)因为保证有序,所以只需要比较前驱和后继哪个更靠近当前查询的数就好了

二:线段树qwq

以权值为下标,建立线段树

维护区间最小值,区间最大值

以当前查询的数为界,看左边区间的最小值,右边区间的最大值比较哪个更靠近当前查询的数

线段树板子2qwq

我们想到求斐波那契数列可以用矩阵乘法,对于ai+x,就相当于原来的数*矩阵的x次方

式子如下:

然后线段树维护矩阵加的和就好了

qwq

在昨天分块的时候说到longlong 以内的数最多被开6次根号,就会成熟。这个题也是利用这个性质。如果这个区间他没有成熟,就暴力开根,如果成熟了,就不要管他。求和就线段树求和。

从5入手emm

要关注小的数或者是取模数,这一般是入手点

我们开一个s数组,s[i]记录mod 5=i的下标对应的数的和

线段树的节点记录对应区间上的s数组

序列:0 1 2 6 7 8 9 11

合并:我们发现合并区间[1,2]的时候,只需要把10000向右转一位即可

即看当前节点对于其父节点来说是左子树还是右子树,如果是右子树就转1位,左子树不转

mex:当前区间内的没有出现过的最小的非负整数。                                                                                              这道题我们要离线做(在线做是真的不好做)                                       

我们按照询问的l排序。接下来我们可以求出1~i的mex(也就是前缀)                                                                我们再考虑1~i的mex和2~i的mex有什么关系。如果a[1]要小于1~i的mex,那么2~i的mex就是a[1]                        推广到一般的r-l的mex与r-l+1的mex,就是让后面大于a[l]的mex变为a[l]就好了辣                                               

 len至少是3,那我们只要找到一个len等于3的等差序列,就有解了

我们开一个bool数组p,p[当前出现过的数]=1

不断枚举作为等差数列中间项的数,检查这个数两边的01是否对称,如果不对称,则证明没有出现的那个数在后面一定会出现(因为是1到N的排列),此时一定有解。如果一直对称,则无解

posted @ 2019-07-15 19:30  千载煜  阅读(370)  评论(0编辑  收藏  举报