本文仅为笔记的简略部分,其中一些较为完整的已独立成文, 请移步至对应文章。
Day1
1.4089: 大嘴乌鸦
桶,前缀和,位运算
我们要找k%(sum[r]^sum[l-1])==0
,我们可以先把k的因子记录一遍,存到v数组中,然后枚举这个因子v[j]
,那么就是找sum[r]^sum[l-1]==v[j]
,根据异或的性质,那么就是sum[l-1]==v[j]^sum[r]
,因此我们只需要找到这个l的前缀异或和出现的次数就可以了。
而每经过一个点,就把它对应的前缀异或和出现的次数加一。
一个数的因子最多大概是个,因此复杂度是。
2.4090: 艾莎
线段树
只需要维护长度为2和3的最大子段,
线段树维护区间内所有长度为2的连续子段中最大和区间内所有长度为3的连续子段中最大。
因为任意一个区间都可以被分成两段不同的区间,这两个区间的平均值一定有一个大于等于原来的区间,另一个区间的平均值小于等于原来的平均值,所以这么不断去下去,直到剩下三个,那么假如分出来的那一个点比原来的大,但是剩下的两个比原来的小,由于一个点不能独立为一个区间,因此要保留三个。
合并的时候分两种情况讨论,
区间加的时候就分别两倍三倍加。
我们每个区间记录一下左端的两个值和右端的两个值,合并的时候比较一下原来两个区间的最大值和交界处新产生的值比较一下取最大就可以了。
3.4091: 沙奈朵
分块
首先题意是要找一定范围内的每一个小区间,每个小区间的答案是这段区间内不同的值的个数(色块数,同一个数为一个色块,色块数的前缀和需要预处理方便区间查询),然后求这些区间答案的和。
采用分块的思想
把序列分成根号个块,每个块长度大概为
维护一个代表之前的每个到这个位置的染色段数
用A[x]
代表当前位置向左所有答案累积和(类似于前缀和一样的东西)。
用B[x]
代表当前位置向右产生所有答案的累计和
用C[x]
代表当前块的x的个数
用D[x]
代表当前块的答案
当前位置向左累计产生的答案为红色段加和。
每遇到一个就需要将其A累加到当前块的总答案中,
每个颜色其实就是的一种状态,
那么怎么转移呢?
算出两个相邻的x之间的答案再乘以之前出现x的次数,累加到A上
正着把这个区间枚举一遍得到A的值和当前块总共的答案,
合并块的时候,左边的块对于右边会产生答案,右边的块对于左边会产生答案,
查询的时候(临时变量ans):
ans+=l.A[x]*r.C[x]+r.B[x]*l.C[x]+l.C[x]*r.C[x]*(!l.r==r.l)
new.A=l.A+r.A*l.C
如果遇到单点修改,那么就重置一下当前块,复杂度为sqrt(n)
,
一开始需要把每个块扫一遍,把每个A,B,C,D都预处理一遍,还要预处理一个区间不同色块数的前缀和,因为从这个点到上一个点产生的答案即为这段区间内色块数,查询
4.4092: 绒绒鸹
树链剖分,DFS序
树链剖分:
dfs序可以保证对任何一个点的子树都是序列中的一段区间。
重儿子:子树大小最大的儿子
重链上每取一个区间都是在dfs序上一段连续的区间
往上跳一次轻边至少会翻倍
那么路径最多就可以通过个区间连接起来
维护一下当前位置的i还要跳几步才会跳到重链头
用小根堆维护f的最小值,再开个全局标记。
习题
DFS序
Day2
1.
none
太简单了没啥好说的
2.
DP,树状数组
求长度为的最长上升子序列的方案数。
代表以为结尾的最长上升子序列,
的值可以由的状态转移过来。
那么我们可以令表示以结尾长度为的最长上升子序列的方案数。
那么如果一个的位置长度为可以被更新,那么它的上一个位置长度为可以更新它,也就是累加上的答案。
因此要更新需要满足的条件是,更新给 的值就是所有满足这个条件的的和。
即图中所有的的值都需要累加。
那么我们可以开一个二元组来维护这个关系,
这样其实就是在找左边的所有的的和。
怎么保证?
是从前往后插入的。
用树状数组优化区间求和问题。
由于,因此需要开个树状数组来维护这个问题。
每次更新第层时,需要访问第层树状数组。
3.
树上倍增,树上区间
表示最后一个选择的区间的右端点,
表示最多能选的区间,优先选短的区间,
对i而言,找到一个最大的j使得区间和为0,即右端点和左端点左边的前缀和相同
记录一个last为上次和i前置和相同的点的位置,
i一定从j-1转移过来么?
不一定,因为上一段不一定以j-1结尾
那就把j之前的区间的f值取个max来更新
那么q次询问就需要通过数据结构来优化复杂度
以每个i结尾的区间最多只有一个有意义,即最小的,
那么总共就有n个区间有意义,将它预处理出来,然后建树
以i结尾的区间for一遍就可以通过last维护最小的这个区间
选完一个区间后就可以选右端点最小的区间
所以说选完以后选下一个区间是一个固定的过程
预处理
这样出来以后就是一棵树
第一个的是左端点大于l右端点最小的区间
然后一直走直到超出r
倍增求解就可以了
T22526 Princess Principal
树上倍增,树上区间
从左端开始不断选最长的区间
从左端不断连边
那就从左边开始不断跳,这样就可以倍增处理了
4.
平衡树,区间求和
不能枚举区间
i为分界点
以i开始的前缀和以i结束的后缀区间
两两组合成的区间,
左边对应最大前缀,
右边对应最大后缀,
只需要把左边的所有最大前缀和右边的最大后缀的和乘起来
求的时候就需要从前往后枚举一遍,边求前缀和边取max
维护一个集合S
S中:维护当前区间除去最大前缀的和
那么这个东西大于0就需要更新
那么i变为i+1后,每个元素都
第二个操作:
找出所有x,y>0
修改为x+y,0
每次插入则修改全局标记
插入的数要减去当前的tag
tag要加上当前插入的数
一开始它们的最大前缀是不一样的
只要后缀被更新一次,那么就不会再不同,那么就可以合并区间
合并以后y的和就可以知道了,
其实就开一个临时变量,然后扫的过程中把最大前缀和的总和记录下来就可以了
记录一下已经合并的区间的数量
这样正着扫一遍,倒着扫一遍,然后两个乘起来就可以了
P2344
树状数组,DP
设表示到的前缀划分成若干个子区间使它
前面有个使得
也就是
的的和。
那就和上面T2类似了,树状数组维护一个二元组求满足上述条件的的和。
P1725
DP+单调队列优化
P3572
飞到i最小的代价为
一个点可以到一个区间
我们倒过来,其实就是从一个区间中飞过来
可以用单调队列维护dp最小值
以dp值最小为第一关键字,树的高度最大为第二关键字,维护这样一个
假如dp值一样,那么高度越高越优
假如dp值不同,那么dp值越小越优(相对于n的dp值)
P5658
树
代表从i到根有多少合法的子串
是由它父亲转移来的
比它父亲就多了个点,也就是多出了所有以i为结束点的子区间
代表以i结尾的到根的合法区间的个数。
假如是(,那么就是0
假如是)
假如j和i这段区间构成匹配的括号串
=
那么我们i的dp值就等于j父亲的dp值+1
我们要维护一个栈使得它dfs的时候和回溯的时候的状态相同
因为匹配的时候我们会弹出两个括号,会导致多弹出,需要记录,回溯的时候再插入
Day3
1.开关
BFS广搜
2.
最小生成树
不需要选出来
加的时候注意一下有没有超过x就可以了
不需要算出来,
3.
最短路
4.
匈牙利算法
点权从大到小排序
贪心选
其它
新矩阵乘法
其实就是限制了边数的Floyed,k为边数
令点双为方点,其他为圆点,然后在它们之间连边,那么最终得到的一定是个树
我的拓扑
缩点
点双 边双
树上差分
圆方树
哈密顿路
竞赛图
DP
DAGDP
Day4
1.
DFS
2.
顺着边 点编号是递减的
从点i右边跳到i号点的概率永远是,所以在所有点中病毒的情况下,让他们排成一条链的概率就是
给定一个的子集,询问若的父亲在中等概率生成,那么从最后一个点到一号点经过所有中的节点的概率,答案对取模。
首先,到的概率是,的父亲是的概率是;
到的概率是直接跳到的概率加上先跳到再跳到,也就是 ,通过数学归纳法得知,右边的点跳到的概率都是。
由于每次跳这些时间是相互独立的,所以原题答案为:
最后一个分数取模不要忘了求逆元。
3.
题意:对于一个无向图,让你给定每条边方向,使得每个节点的入度为偶数。
首先,士兵人数一定要是偶数,否则不管怎么分配也一定无法分全。
也就是边的数量为偶数。
对于一颗树,叶子节点是绝对不能去的,因为这样不可能是偶数。那就从叶子节点开始,士兵向上走,如果当前点士兵数是奇数,那就让它上面的边的士兵向下走到这个点,否则就向上走。
这样就可以维护所有非根的点的士兵数都为偶数,由于士兵总数为偶数,那么根节点最后也一定是偶数。
但是这样一张无向图不一定是树,所以我们可以建一颗 DFS 树,无向图中, DFS 生成树仅存在树边和回边,所以对于每个回边,我们让它回到深度更大的点,剩下的就和树上是一模一样的了。
4.
有一些从右向左扫的激光炮,假如打到了防护罩,那么这个激光炮就会失效。
对于第个设备,我们定义,也就是能打到第号点的激光炮中右端点最小的,那么我们在这里放一个防护罩就一定可以保护不受攻击。
而在中选取一个,那么,这是因为是一定覆盖的,若没有比这个还小的,那么,否则就会比更小。
而对于,那么,因此我们可以得出:所有的区间要么互相包含,要么无交,所以就可以根据这样的关系建一颗树:
在树上进行以点权和为标准的长链剖分,假如一条路径上放了一个防护罩,那么这条路径就是安全的,因为装置在区间的最左端,这样每次贪心地选个长链就可以使保护的权值和最大,从而使破坏的权值和最小。
树上差分
一条路径,对于叶子节点+1,终点的父亲节点-1,求和只需要求终点的子树和就可以了。
LCA小技巧
比如要求黑色点和粉色点的的深度,那么就可以把从到根的路径上的点权都加一,然后求到根的路径上点权的和。(初始点权都是0)
那么对于,我们就可以每次把到根路径上的节点点权加一,最后统计到根节点路径点权和,这样就可以把所有的深度和求解出来。
而对于就可以通过前缀和求解。
长链剖分
长边组成长链,
至少能查级祖先,为该长链长度,
一个长链肯定能跳到下一个长度和它相同的下一段长链。
那么假如那么就可以很轻易地查询级祖先,
类似ST的预处理,我们令,那么我们先跳步,再跳步,那么就可以跳到了。
假如第二次跳出了当前的长链,没关系,还可以跳到下一条长度同样为的长链的段,这样就可以查到了。
树的重心
所有点到这个点的距离之和最小,那么这个点就是重心。
树的重心就是从上往下遍历找到的最深的满足子树点权和的点。
那我们把这棵树给序列化,枚举它的前缀区间,直到找到第一个右端点使得,那么现在就有两种情况,第一种就是这个点在重心的子树中,第二种是它不在重心的子树中。假如它不在重心的子树中,那么中心的子树要么在左边的区间,但显然不可能,因为在之前的区间都不满足的条件,那就只可能是在后面的区间,后面的区间那就从一个非零的点开始找了,否则就找刚才加入的最后一个点(即满足条件的最小的)。
Day5
1.
令表示走到异或和为且还剩颗珍珠的方案数。
递推式为:
最后答案就是。
2.
定义是从以为根的子树中选出单调递减的路径的方案数,是从以为根的子树中选出单调递增的路径的方案数。
对于每个节点一开始值都为,因为它们还没有假如任何其它节点,只能选自己的方案数为。然后开始,在回溯的时候处理,在的子树内。
递归的时候选取一个点作为要选的两个点的 LCA ,然后选取离这个点最近的两个点设为,那么我们就需要首先合并和,也就是更新的信息(),然后合并完之后这就是一个新的集合,那么就可以把它和以为根的子树进行合并,合并的时候分别枚举两个集合中的点,假如,那么答案累加就是,因为是的一条路径,所以到是单调递减的。相反地,如果,那么就是。
先合并 lca 和它的第一个子树,也就是目前知道子树的信息以及根节点当前的信息,很显然没有其它子树,它的信息不会再被更新,而还是有其它子树的,也就是说它的仅限于当前遍历到的子树,即。
那么我们再枚举其它子树,实际上就是不断合并连通块更新信息的同时并累加答案。
这样全部合并完后,就是答案。
3.
数位 DP
首先题意是让你找出所有的序列,这个序列满足长度为序列和等于,序列中的每个数都满足的范围,该序列的价值为该序列中所有的数的异或和,答案是所有情况的异或和的求和。
我们可以来看序列中个数和的二进制表示,定义为考虑完前位,且第位的和等于的这一位,下一位进位为的方案数,表示考虑完前位,且第位的和等于的这一位,下一位进位位的所有方案异或和的求和。
因为我们要保证序列和为,所以我们要保证二进制下每一位加起来都等于的这一位。
然后我们枚举一个代表第位我要在中填多少个1。
那么,其中为组合数,意为原来的方案数,在这位又产生了种可能,由于相互独立性,因此下一位的方案数是它们相乘。
假如是偶数,那么这一位异或起来一定是,所以不会产生新的贡献,有的只是新的情况,所以就相当于有若干个加了起来组成了新的状态,也就是:
假如是奇数,那么这一位异或起来一定是,而第位加一,产生的贡献是,所以会产生情况数每种情况的贡献,也就是:
所以最后的答案就是。
4.
类似地,每个人会去能去的档次最高的店,定义为只考虑这个区间里的人所能得到的最大收益是什么,枚举标准最高的店,定义为有人到第家店,为能到达的人数,那么。
表示合理选择第家店的档次使得收益最大的收益。
接下来就是求出所有的,用表示的花费,代表有个人来的最高收益,也就是。
,
2比1优的时候,往后2都会比1优,由于2的斜率比1斜率大,交点之后2更优
假如
这样处理完之后函数的斜率是单调递增的,
往后优解不一定是连续的,优先选斜率大的。
所以维护的是所有可能成为最优解的斜线,被覆盖的没必要维护(覆盖可以通过函数图像的交点位置来判断),维护可以用单调栈。
最后处理出来就是一个永远最优的分段函数。
快乐
定义为完全包含在区间内的线段的个数(人数),这样就可以线性求完全包含在内并且包含我枚举的的一个区间,也就是 ,为我枚举的假设最大值,让这些能到的区间都到,然后求一下这段区间。
凌乱
如何选择一种切法使得这个矩阵凌乱度最小
,
也就是说是单调不降的,
所以最大就是
左上角,右上角,最大的数满足(左上角右下角,矩阵凌乱度,
假如横着切,上下两个矩阵都小于等于l-1
下面
竖着切:
因为就要切在下边界较小的那一个矩阵,因此要取min
是先增后减的。
相当于面积相同的情况下,宽越大,高越长。
在中间竖着切了一刀,左边随着宽不断增大,高是不断减小的,右边相反,
所以的第一项是单调不增的,第二项是单调不减的,
加上一个取min操作,将会存在一个分界点,使得其图像为一个单峰函数,
设分界点为,左侧第一项大于等于第二项,右侧第一项小于等于第二项
因此是先增后减的,可以通过二分查找这个分界线,然后比较它上面和下面的值谁更大。
P9387
这样就可以快速处理出的值。
我们设表示我考虑了二进制的前位,当前位将向下进位的值是,表示是否大于:,表示前位是否大于,此时的方案数。
(大于或等于)
那就可以从低位到高位进行 DP 了,初始时
转移方程是:
这里。
代表的当前这一位的值,由于要满足,因此设,。而这位的分别枚举一下就好了,选出符合这个条件的进行转移。
个二进制数加起来最多进位,三个数最多进位。
至于,假如下一位小于的下一位,那么就是小于
,为,假如下一位和相同,那就继承上一个状态,因为还要接着比下去,如果下一位比大,那就是大于,为。
最后的答案就是,因为的二进制位数不会超过位。
Day6
1.
2.
3.
4.
卢卡斯定理
对于和质数,有
时,,所以第位为1时,的第位为1,的第位为时,的第位为,所以。
欧拉函数
相乘即得。
P2261余数求和
对于,求。
来求解一下递推式:
。
这里第一项之所以枚举到是因为枚举到会使所以就省掉了。
接着化简:
然后就可以线性递推了。
原T4
求
那么我们先求一下
那么这个是什么呢?
实际上是
因为这样实际上是枚举了
相当在原来的枚举基础上少枚举了 但是多枚举了
那我们接着往下处理这个式子
这样我们就得到了递推式
错排
对于每个,求出有多少长度为的排列,使得。
我们设某个位置使得,那么就分情况讨论:
- 假设,所以现在有两对错排已经确定了,那就是从剩下的个数中选错排,而的位置有种可能,也就是。
- 假设,我们把移到的位置,也就是说有,所以有一对错排已经确定了,还剩对,那么就是
于是。
解方程
计算的解的个数。
隔板法
把一个1看作一个小球
间隙插隔板
分组
组合数
实际意义
选代表元
设
每一对对应着一个代表元,即方案
设都为
那就是
y都加一
设
翻折法
遇到函数图像就把剩下的路径翻折
这样既可以求解不合法的方案数了
Day7
1.
根据相对关系分三种情况讨论倒推出之前的位置。
2.
每段的下一个与当前字符的关系都是相同的,
其实就是字符匹配,KMP
3.
质因数分解
对于每个质数的指数相加起来,判断每个指数是不是k的倍数就可以了。
那么可以
维护每一位模k等于多少。
把每个区间维护成前缀和的形式,
那么区间求商那就是指数相减模k等于0
所以就是两个前缀模k意义下是相等的
维护一下模意义下的哈希就可以了
每一位都是0~k-1
递推的时候加一个数就进行哈希处理。
那么怎么对一个数快速进行质因数分解,
先用线性筛预处理出每个数的最小质因子,然后对每个数分别除一下就可以了。
哈希值要开大一些
4.
Alice n/2上取整
Bob n/2下取整
可以dfs记忆化搜索一下。
对每个串进行赋值,那么就可以直接进行点权相减
这样能保证两个点加起来正好lcp被加了一遍
这样sort一下然后计算即可
KMP
字符串严格匹配
前半部分和后半部分
假如已经知道了一个长串的border,那么加上一个字符,那就继续比较下一个字符
这么说同时去掉最后一个字符也是相同的。
对于一个更长的串,那么它的border是被比它短的border更新而来的。
也就是用短串的border推到长串的border
设nxt_i表示长度为i的前缀border长度
一个串可以有很多border,排序后小的border是大的border的border
那么就维护每一个border的状态
从下往上枚举看看哪个border的下一个字符是匹配的
字符串匹配
判断m在n中出现了几次
一个是hash,一个是kmp
P3193
dp_i,j表示考虑完前i个字符目前匹配长度为j,匹配可以用KMP
只要保证j<t就可以保证
然后把递推转化为矩阵快速幂就可以了
hash
比较两个东西一不一样。
类似于编码的过程。
对于字母串,每个字符串当成一个26进制的数,b=26
这么大的数肯定是存不下的,那就找一个大数取模
有的概率相等。
优点:
判断t在s的某个位置是否出现
如何快速得到字串的哈希值?
前缀和预处理
也就是区间求和
P2312
让左边右边同时模上一个大质数,然后我们求一下在模意义下是否等于0就可以了。
可以设多个模数,增加正解的概率。
P5537
对于走到每个节点,路径会产生一个序列,这个序列是唯一的,
每一个点都对应一个操作序列,
并且这个序列都唯一确定这个节点。
对于操作序列,若完全相等,那么到达的点相等。
每次对于这个操作序列进行一个二分。
这个具有单调性,因为假如到这个操作序列的某个前缀已经走不通了,那么后面的也一定走不通。
所以我们要找的能走通的序列长度最长值。
对于每个点都处理出它对应的操作序列。
根节点是x?
也就是先到x再到要到的点,再进行拼接。
可以用线段树维护,可以合并两个哈希值
Manacher
中间点很重要
一个回文串两边同时操作可以改变长度
来二分一下这个最长长度
回文中心
回文半径
abcba
!#a#b#c#b#a
感叹号是边界
b#b
这样回文中心就很显然了。
核心思想:求每个点的回文半径
回文半径即对应回文子串的长度
假设之前有串覆盖到了我这个点,那么就可以对称过去,最大为覆盖区间的右端点,最大继承到右端点,也就是继承对称点回文半径的信息。
对于这张图,红色点的回文半径已经求出来了,也就是对应红色的区间,下面我往后枚举,发现要求黑色点的回文半径的时候,这个点在红色点的回文半径内,这就意味着红色区间内有点与其对称,而对称的灰色点在之前已经被处理过了。
所以我们就可以继承灰色点的回文半径,也就是说我们不用再枚举一遍了,少走几十年弯路。
假如灰色点的回文半径并没有超过红色点,那么黑色点的继承到的回文半径也不会超过红色区间。
假如灰色点的回文半径超过了红色点,那么黑色点的继承到的回文半径只能到红色点的回文半径的边界,因为再往外黑色点和灰色点就不是对称的了。
因此总结下来就是继承到的回文半径的位置要和红色点回文半径的位置取个。
HDU5371
给定一个长度为n的数列 ,现在要求你找出最长的形似的子串,是的反串。
两个回文中心。
只要两个回文半径可以互相覆盖到彼此的回文中心,那么就可以满足条件。
使满足这个条件下使两个回文中心间距最大就可以了。
P6216
在中哪些位置出现了可以用 KMP 。
对每个位置处理出是否可以从这里开始到完全匹配,
也就是对于每一个匹配的位置,我在它匹配的左端点的位置加一。
查询时右端点要减去的长度,这样就可以保证区间内的“1”都是合法的,这样就可以去找区间里1的个数来计算出现的次数,
然后枚举这个点的回文中心,
所有回文子串的公差为,
右端点同时减去的长度后公差依然是1.
左右端点都是等差数列。
可以用前缀和优化:
然后我们其实就是要求剩下的这个三角形中所有点的和。
P5446
时,满足条件的一定是的前缀,所以我们枚举 的前缀,
若对于每个枚举的,对它进行不断翻转,若得到的不是的前缀,那就跳过。
对于最后一步,是关于右端点为中心的回文串,判断一下即可 ,如果可以就可以判断这个 R 串是否可以。
Trie树
树形结构,
从根走到底那就是一个字符串,
可以插入字符串,
没有出边就新建一条,
所以就可以用一棵树表示这个集合,
即一个字典树,
可以求 lcp,
即两个点 lca 的深度。
对于这颗Trie树上的字符串和,它们的lca是,的深度是,因此它们的的长度就是2。
01Trie
各种操作一样,字符集变成了0和1。
可以进行二进制操作
这样就可以对某个数得到异或值最大。
因为假如一个树二进制表示为,那么我就可以贪心地选择每位,如果可以我地最优选择是,假如某一位上没有我想要的(序列中没有该数),那就别无选择,但这样一个流程下来我得到的是最优解。
进行一个贪心
P3065
建一棵树,
把小于号换做有向边,
跑拓扑排序,
出现环了这条路就走不通,假如之前已经有,
后面又从,说明,那么就产生了矛盾,那么这条路就不能继续往下走。
CF178F3
树形背包问题。
个节点转移,其它的节点复制,因为只有一个儿子
P4551
把一条路径拆成两段,
那就是到根的异或和异或上到根的异或和,
这个到根的异或和可以 dfs 处理一遍,
要找到一个异或上最大
对一个数求异或和最大,请问另一个数?
那就可以建01trie了。
可持久化
求建出trie树
版本思想,
我有次修改,那就有个版本的 trie 树,
插入字符串的时候,新建一个部分 trie 树,这个trie 树上只保留信息改变的节点,也就是新加的节点和被影响到的原来的节点,
每个版本的trie树只需要记录根节点的信息。
对于没受到新节点影响的节点,直接对它们连接即可,
原来的查询可以照常进行。
P5283
枚举右端点,找到使得异或和最大的左端点。
对于找到一个
边查边插入右端点,用可持久化线段树。
最大值被选后,次大值才有可能成为答案,类似于超级钢琴
维护l,r,按照v用优先队列排序
大区间就会变成两个小区间,区间内的异或最大值可以用01trie解决
Exkmp
维护最右端点使得之前的lcp延伸到这个端点。
黑色为原序列,我现在要求每一个后缀与原序列的 lcp,即最大公共前缀。
比如我现在要求粉色点的 lcp,
但是发现这个点在当前最大lcp的右端点的范围内(红色线),即我们知道了灰色与黑色的 lcp,而灰色与蓝色的粉色部分又相同,于是蓝色和黑色的粉色部分也相同。
而对于粉色部分,我们已经知道了绿色与的 lcp,绿色<粉色,相同位置的绿色=粉色,于是绿色的 lcp 就可以被粉色点继承;假如绿色>粉色,但是超出部分在蓝色部分中不一定相同,因此最多能继承到粉色的边界处,因此绿色的 lcp的右端点下标要与粉色右端点下标取后继承。
而对于字符串与字符串的每个后缀的 lcp ,就可以把接到的后面,然后从长度为的后缀开始扫就可以了。
每次维护的就是当前 lcp 延展到的最大右端点以及当前的递推数组,也就是用到的绿色的 lcp 的长度。
后缀长度由长枚举到短,第一次没有可以继承的,那就只能自己扫一遍了。
P7114
C即为
C是确定的,C中奇数的个数很好求,
然后枚举一个AB的长度,也就是循环节的长度,再枚举一个循环的次数,
若,那么即循环节,用这个判断我们枚举的AB长度是否合法。
AB会在这个循环节内出现,A出现的频率为,所以就枚举所有的A,
然后用桶记录每个奇数出现的次数,加起来,看看是否满足,那就累加答案。
Day8
1.
树形 DP
设代表从出发到其子树内最长链的长度。
转移就是
2.
用代表从出发到的路径条数。
可以拓扑排序并转移。
若是的一个前驱,那么
由于我们只关心奇偶性,因此只记录0 1就可以了,转移就是。
用bitset维护就可以了。
3.
表示让值为1的最少子树中的个数。
看下是否大于1的个数。
4.
前i个数已经分成了j个集合的方案数,转移:
树上?
按照bfs序 DP
要么就加到剩下的集合,要么就自己新开一个集合。
从上到下 DP。
这样就能保证DP到一个点的时候,它的祖先已经处理好了。
P3304
求有多少条边满足所有直径都经过该边。
- 直径是一定有交点。
- 如果分叉了,那么两端是一样长的,否则就可以有更大的直径。
- 两个树,两个直径,将两棵树的根节点连接起来,一共可能有六条直径。
从树上某个点dfs,深度最深的点一定是某个直径的端点,
查路径长度直接查LCA就可以了。
先找出来一条直径,
从右端点 DFS,
对每个点记录它到子树里最深的一条链,
然后如果和当前点到右端点的长度相同,那么就可以替代,
执行完所有的操作后主链上剩下的就是公共的边。
P3177
一条边被多少个点对经过就是它的贡献。
其实就是这条边左右两边黑色点的对数加上白色点的对数。
设表示在x子树内选了t个黑点,最小贡献是。
新加子树y,
合并的时候
n方个点对,那就是n方
P3174
表示从x开始向子树里找一条链使这个毛毛虫最大。
枚举儿子
在lca处合并
找最大的两个
父节点
CF1153D
表示x为1儿子最小填1的个数,
对于max和min分别讨论
设设x在子树中有s个叶子,x最多能排多少名。
1:
0:
P3523
对K二分,
也就是以每个我选的点为中心,K为半径形成一片覆盖区,如果能全部覆盖全部关键点,那这个K就可以满足。
如上图,红色点为关键点,蓝色点为我选的节点,蓝色⚪为当前覆盖区,显然,上图为一种合法的方案。
对于每一个点和一个K,一定是让一个关键点再覆盖范围边界被覆盖到是最优的,因此我们要让这些关键点刚好卡上这些边界,这样就可以使它可以覆盖到更远的点。
所以我们定义代表x子树中最远还没有覆盖的点的位置,如果这个距离等于K了,我就在这里选一个点。
定义代表覆盖到的最近的已经选的点。
如果某个关键点到的距离即,那么这个关键点就会被波及。
那么就可以更新
1453
基环树
树上问题是求解最大独立集
设x这个点选/不选,子树的最大权值
选了那就是x的权值加上儿子都不能选
对每个子树做一个DP,每个点对应选这个点对应的权值和不选这个点对应的权值。
P4381
设表示从到其子树的最长路径是多少。
断环为链,记录链上的路径前缀和,答案就是
枚举 并维护的前缀最大值
CF187E
选了根节点之后,答案就是每个点的子树大小之和。
换根DP
把x换到儿子y上。
P2986
换根DP
P4438
对每个点记录x y有多少,
x节点,j个l,k个r的贡献大小。
P2014
在x的子树中选了大小为i的子树得到的最大价值
只要下标小于等于,那么就是两两配对的
LOJ160
dfs 序——后dfs优化背包
先 dfs 子树,再把根计入。
只有递归完了子树,根节点才会被假如
dfs序到了x在这里,总递归完的子树为i的方案
P1080
交换:微扰法
把所有的人的排序
P3574
设为只走x子树中的点,所求的最小值。
类似于国王的游戏
越小越放到前面。
Day9
NONE
Day10
1.
2.
设为从某个1开始走到i,j的最小花费。
而每一层点特别多的时候就可以用多源最短路。
令
然后就可以将第一维排序,
然后查询的时候就相当于查询第二维的区间最小值,这样就可以用线段树或者树状数组来做。
插入点的优先级要比查询的优先级高。
就可以分成两个象限来处理,。
3.
暴力枚举点对可以解决。
可以先把一个点和它所有互质的点都预处理出来。
对每个点维护一下到根有多少互质的点。
然后就可以用树上差分,预处理,查询。
我们只关心质因子的种类,而不是个数。
那么其实就是两个集合的交集为空。
容斥?
加入元素?
删去元素?
莫队
一个序列,长度为n,每次区间询问。
先把这些询问按照某种方式打乱顺序。
pair
KD-Tree
Unordered Map
Day11
1.
按照a排序
维护前缀和
枚举r的同时更新与l有关的变量的最小值
2.
令f为长度为i有j个顶的个数
每次插入新排列的最大值,
插入再原本的顶旁边顶的数不会变,
最左边和最右边也不会变,其它地方顶的数量就会加一。
3.
将点按排序。
表示第以i个点为端点向左/向右的方案数。
4.
假如,在满足的情况下,,这个时候以作为前两位更优,因为大小和第三个数的选择范围;
假如,中间存在一个满足,这个时候更优。
所以这个时候对一个点,选左边/右边的第一个比它大的数更优。
有用的前两个数只有个。
对询问按照左端点从大到小排序。
对序列从右向左扫描,每次找一个c的最大后缀,也就是找到的最大值,来和我当前的这个点匹配,而某个数,所以是最大后缀,用线段树维护。
P8819
给每个点随机一个权值,
统计所有起点的点权和和所有点的点权和。
如果相等,那么说明符合。
那么就维护每个点以它为终点的激活边的起点的权值和与每个点以它为终点的失活边的起点的权值和,然后每次进行这种操作的时候给对应加上或者减去对应权值和即可。
Day12
1.
海底高铁
2.
同Day7 T1
3.
每个点记录以它为左上角/右下角为顶点的正方形的最大边长,
那么边长区间就是 。
如果组成正方形,那么两点处于对角线位置,再判断左上角的长度是否合法和右下角长度是否合法。
三个条件:
所以就枚举对角线,算对角线上的两个点
枚举,看有几个合法的
那就分别算和所有的点,查询。
可以树状数组
4.
又是全排列
代表考虑了前i个数,选择情况为j(二进制下表示,0代表未选,1待选已选),这种情况下的逆序对数。
状压 DP