【一些总结】
总结
数据结构:
\(DS\)题不要依赖板子,要自己写,包括各种图论树论数论的题,考场没有板子给你看。
平衡树与线段树
平衡树/线段树五问:
1.每个节点需要记录那些信息
2.需要那些标记
3.下传标记怎么做
4.区间整体修改怎么搞
5.如何合并区间
注意tag操作的long long
线段树记得返回值啊。
下标是那个你要维护偏序关系的域
如果有对于一个区间的操作,如果端点数较小,考虑离散完丢线段树上处理
\(Fhq_treap\)在维护名次树按\(v\)分裂,维护区间按子树大小。
启发式合并。
树状数组
树状数组注意值域大小
尺取
对于一类\(max - min\)的题目,可以考虑按权值排序后尺取。
CDQ分治
cdq分治注意左子区间,右子区间,本区间的更新顺序,考虑是否左子区间对右子区间造成的影响是否会影响右子区间本身的更新,如最优化问题
堆
k个最值的求法,先对每个元素进行最操作,再对堆顶进行次操作丢入堆。
考虑堆实际上一个,排序完后依次插入树的一个结果。
启发式合并
对于数据结构的合并,大概是暴力合并每个点?
考虑把小的合并到大的来,进行一个启发式的合并
这样复杂度是\(O(n min(x,y)) = O(nlogn)\)
multiset:
multiset删除未出现的元素会出现问题。
区间询问问题
把询问看做二维平面,扫描线做
区间询问差分
可持久化操作
偏序问题
考虑答案性质,对点维护答案贡献子区间,然后区间查询查询子区间贡献
重复操作问题
考虑kx + b形式利用矩阵
考虑一堆操作,区间查询,倍增。
若干函数单点取min
可以使用凸壳维护,不过容易出锅,建议使用李超树
树论:
直径
树的直径的一些性质:两颗树由一条边连在一块,则新树的直径,定为原两颗树的直径的两个端点取二
就是找一个最远点,让\(dis(u,k)\)最长,在树上的话,我们知道这个点\(k\)是这颗树上直径的两端之一。
一些结论
区间移动,求rk,可以考虑在 \(n + m\) 的序列上操作,用log的数据结构求rk。
树上距离问题
考虑树上的关键点群的路径和可以通过用dfn排序,相邻距离和除2来求
树上子树查询问题
dfn序上操作
启发式合并
二维平面操作
路径问题
树剖
树上差分
看到树上路径,考虑拆成\(u->lca>v\)两段处理。
LCT
路径满足某种性质,则考虑利用性质依次加点例如树上笛卡尔树
树上莫队
对dfn序操作
在树上按dep 根号分块
背包
上下界优化
树上构造题
考虑叶子是否能够强制只有一个状态满足条件
考虑转化到dfn序上问题
考虑直接上线段树合并
考虑度数
树上dp
启发式合并优化
ddp(改变权值最大联通块)
一些特殊性质
线段树状其树高log
随机树期望树高根号
限制子树dep,则按dep加点
字符串
子串
考虑数满足某个条件子串的数量,可以从结尾统计
tire
注意tire树的空间。
hash
静态相等问题可以用hash做。(比如判数组相等问题,可以对每个下标随机一个权值)
思路
两种对于字符串匹配有包含关系的结构:
AC自动机的fail树
后缀自动机的后缀link树
点是边的多一。
细节
strlen是O(n)
图论
基础
在图上遍历要写
for(int i = head[u];i;i = e[i].next)
差分约束系统:
在构造时要对构造出来的系统保持清醒认识。
在判负环时可以考虑牺牲一些正确性换取时间
园方树
圆方树记得不要退栈到\(u\)
tarjan
割点:
low[v] >= dfn[u] and !(u == 1 && son_size == 1)
强连通分量:
要记得维护在搜索树上的点,low,dfn只在他们中间更新。
dfn[u] == low[u],出栈到u为一个强连通分量。
即已经确定了联通分量的不在搜索树上。
点双:
low[v] == dfn[u]
退到v,加上u是一个点双
点双:在块内 ab 两点之间一定存在两条点不相交的路径
边双
low[u] == dfn[u]
退栈到u
边双中能够满足以下条件:
对于任意对点对,都可以调整无向边方向为有向,保证有\(s_i \to t_i\)都成立。
边双:在块内 ab 两点之间一定存在两条边不相交的路径
最短路
改变中间路径求最短路,但起终点不变可以以起终点两边dij
最值计算可以考虑图论模型。
最短路树
等边权无向图其最短路树为BFS树
二分图匹配
二分图有最优匹配的条件:
\(\forall V \in S,|V| < |N(V)|,N(V) = \bigcup_{x \in V,(x,y) \in E(x)}y\)
图上删环问题
只要删搜索树上返祖边即可删完
图上路径查询
重构树
按性质加点并查集维护连通性
bitset硬来,\(n^2/w\)
动态规划
优化
斜率优化一般不用设初值,但如果有多层dp,建议跑第一层dp初值,单调队列\(head < end\),只要觉得自己柿子没推错,就相信自己。
考虑数据结构优化
考虑更改状态
状压
考虑顺序枚举,实际上可以用状压解决从\(n!\)到\(2^n\)的复杂度
子集dp
枚举子集:
for(int S1 = S;S1 != 0;S1 = (S1 - 1) & S) del(S1);
细节
控制转移次数的最小可以每次减去一个INF
当答案小时,可以考虑更改状态,设能否到达,将最值问题转为可达性问题。
数论:
基础
\(\sum (n/i) = nlogn\)有点蠢
容斥
考虑容斥,考虑一个和式求解的柿子可以考虑插板法。
矩阵乘法
对于矩阵乘法,多次询问,单次是倍增做法的,可以考虑记录一下每个倍增点的矩阵,少个n。
鸽笼原理
考虑到有\(m\)个限制,可以考虑\(m+1\)个最大的答案,这样可以利用鸽笼原理得到答案。
计数
方案数的次方,可以看做多个单独操作的合并
正难则反,反面会很好做(在共集问题中)
取模问题
\(a^{x1^{x2^{x3....}}}\pmod{p}\),可以在\(log(p)\)次的暴力用扩展欧拉定理解决。
二项式反演
我们设\(f(i)\)表示钦定\(i\)个,其他不管的方案数,\(g(i)\)表示恰好有\(i\)个的方案数。
那么就有\(f(n) = \sum_{i = n}^m\binom{i}{n}g(i)\)
二项式反演一手则有
\(g(n) = \sum_{i = n}^m (-1) ^ {i - n}\binom{i}{n}f(i)\)
一些结论
最小权覆盖集 = 全集 - 最大权独立集
正难则反。
Q:有\(k\)个点要统计所有点对的一种值。
怎么怎么在logc次分组内遍历所有点对。
A:对二进制下每一位都按0,1分组。([GXOI/GZOI2019]旅行者)
每个元素有0/1状态,翻转状态看成异或。
Dilworth定理:最长反链=最小链覆盖=最大独立集
一个数对\((a,b)\)可以考虑取出\(gcd\),让他们两个互质使条件变强
类\(kkkkk\)这样的数可以表达为\(\frac{10^n - 1}{9} * k\)
但是如果我们做大于等于的话,就只用一遍\(dp\)即可。
然后我们做这样一个过程:
\(\sum (A_i - A_{i - 1}) * g\)(WC2021),前缀和和恰好的关系。
区间问题
考虑\(|a - b|\)转变成\((a,b)\)区间长度
数个区间求最长交集,考虑按\(l\)排序,维护最大的\(r\),考虑答案为\(max(min(r,r_i) - l_i)\)
细节
快速幂不要溢出负数,直接全开ll
贪心
一些结论
有多个相同对象操作求最值考虑差分并贪心。
二分
考虑比较关系的,可以考虑二分一个中间,把他转换成01串来操作。
反悔贪心
即每次贪心时给一个反悔路径
杂项
思路:
碰到类似于\(2^n\)的东西往二进制上靠
遇到类似于一个点对一段区间有贡献的题目,往树上想。
字典序最大的一类题目:
正解则是考虑按位确定:考虑从 \(1\) 到 \(n\) 依次确定该点选了哪个值,确定 i 这个位置的值时,我们选取满足以下条件的值:
- 使得这个东西确定之后,存在一种 \(i+1\) 到 \(n\) 的分配方案,使得总方案合法。
- 尽可能大。
考虑抽象模型
如果两个函数一个单调增,一个单调减,如果两个函数相交是理论最大值。
但是由于两个函数是离散的,可以考虑这样一个东西。
那么答案是如下两个\(k\)的最大值:
\(f_1(max(k)),f_1(k) < f_2(k)\)
\(f_1(min(k)),f_1(k) >= f_2(k)\)
由于函数性质,所以可以进行二分。
看到区间形式,考虑是否能够进行前缀消除转化为[1,l - 1],[1,r]的操作,如异或粽子
对于一些无法正面求解的问题或者条件,考虑正难则反。
或者先对一些易满足的条件进行满足,在这个基础上进行调整。
对于\(n^3\)复杂度的东西,矩阵,区间dp考虑一下。
最大前缀和等价于:
前缀的后缀没有小于0的
后缀的前缀没有大于0的
解决最优化问题的一类方法:
网络流,费用流
贪心
动态规划
做一个不满足题目约束条件下的解,并考虑调整他。
LIS和LDS的长度一定有一个大于等于根号n来着吧。
考虑在序列上多次询问完成目标要多少次操作,可以倍增思考。
细节:
long long long long long long long long long long long long long long long long long long
注意数据范围,\(long long\)是否要开,数组是否够大
快读时如果保证没有负数可以不写判负数的程序,能快不少。
注意哪些地方要\(ll\),哪些地方不用,\(int\)和\(ll\)速度差很多,常数差就在这里。
low[u] == std::min(low[u],dfn[v]);
这个是不会报错的(要写单等号)
不要写错东西,思路要清晰,过了样例并不能说明什么,一定要记得对拍
要敢于写代码,不敢做就永远跨不出那一步,你明明能做的很好。
数组A[N],下标只到N - 1,别被RE送回家
对于某些操作使答案变化的题,如果没法直接维护,可以思考是否有一些性质的操作能让答案变得更好。
指针移动完可以考虑再向两边扩展一下保证正确率,甚至可以让假做法过去。在明白有单调性但不是很清楚时可以用。
指针移动要考虑移动的范围,不要出界。
数据量大就记得快读,最好养成大于\(1e5\)就快读的习惯。
取模最好手动实现,因为直接取模真的很慢。
在函数里定义变量记得赋值。不然会出现随机的数据
众所周知sqrtt是一个跑得极慢的函数
所以我们可以两边平方消掉sqrt,最后再算一次即可
就是如果一个东西有很多操作会贡献,然后一个东西只贡献一次, 我们强制他贡献的时候只在第一个把他贡献的位置计算。
自信
21-11-17补
区间询问问题
数据结构
倍增
硬做,可能复杂度是对的
最优类问题
数据结构
抽象模型,考虑其有贡献需要多少代价
动态规划
网络流
贪心及反悔贪心
构造方案
二分,转成可达类问题
拆贡献形式
可达类问题
并查集
图论中直接缩点或者\(\frac{n^2}{w}\)
考虑依次加点维护动态问题
线段树优化值域可达性dp
构造一种可达方案
构造类问题
手动构造
转换为图论或者树论问题
带权二分(不建议使用
数论问题
转换为函数问题
重复操作问终态
矩阵乘法
倍增
数据结构直接硬维护
对待一些结论题的做法
先从基础的结论入手
大胆猜想,手动构造验证
直接证明
求全局和类问题
树上换根
考虑快速对每个单点求和
考虑拆贡献
扫描线,固定一端,求其他全局和
验证性问题
考虑直接比对
考虑最小循环同构
考虑随机权值hash比对
考虑优化比对过程(kmp)
计数问题
考虑直接dp优化
考虑数据结构
考虑容斥
考虑补集(在询问有交集集合个数是有奇效)
高维前缀和可处理子集贡献问题
数学问题
考了就寄。
考虑数对(x,y)取出gcd,强化条件
考虑一个数的贡献
考虑清晰写出表达式,化简
二元方程同样可以使用扫描线
考虑很多时候直接搜索由于有上限限制,所以复杂度是对的
积性函数
几何问题
考了我吃。
其他问题
没啥好说的
胆大心细,记得对拍
考的都会,不会的都不考。
相信自己的水平吧,来走了一遭,不再后悔。