临时抱佛jio
需要打的板子
高斯消元
FFT/NTT
拉格朗日插值
LCT
splay
K-D Tree
二分图/最大流/费用流
后缀数组后缀自动机
可能还需要重新学一下的东西
整体二分
Polya定理
dp
区间dp
对于满足
\(f[i][j] = min(f[i][k], f[k + 1][j])\)的dp方程,猜想其满足四边形不等式
设\(s[i][j]\)表示\(f[i][j]\)取到最优值的决策点,有\(S[i][j - 1] \leqslant S[i][j] \leqslant S[i + 1][j]\)
状压dp
转移状态不是太多的时候可以预处理所有有效状态
对于LCS这种每次只有一个会改变的dp可以记录差分之后的状态
数位dp + LIS 可以通过状压01来还原状态
对于一类有后效性的状压dp可以考虑SPFA
- 给一个序列,对每个\(i\),求a[i] xor a[j], a[i] or a[j]的最大值可以暴力记录子集记忆化
void insert(int x) {
//if(mark[x]) return ;
mark[x] = 1;
for(int i = 0; i < 20; i++)
if((x >> i & 1) && (!mark[x ^ (1 << i)]))
insert(x ^ (1 << i));
}
枚举子集
for(int i = S; ; i = (i - 1) & S) {
if(!i) break;
}
MinMax容斥
全部访问->第一次访问
\[E(\max(S)) = \sum_{T \subseteq S} (-1)^{T + 1} E(\min(S))\]
高维前缀和
统计所有\(i \& j = j\)的\(a[j]\)之和
实际上就是每次枚举一位然后算之前位的所有子集的贡献
for(int i = 0; i < B) {//必须先枚举这个
for(int j = 0; j < (1 << B); j++)//下标必须从0开始
if(j & (1 << i)) f[j] += f[j ^ (1 << i)];
}
期望dp
通常是记录到达终止节点的期望步数
期望 = 权值 * 概率
发现期望dp中只与前一轮状态有关时可以矩乘优化
树上期望dp可以直接拆dp方程变成推系数的形式,先把叶子节点和非叶子节点拆开,发现1号节点不用从父亲转移过来从而解方程。
背包dp
枚举子树dp的复杂度是\(O(n^2)\)
上面的dp如果是卷积形式可以用NTT优化
树形依赖背包可以用点分治+dfs序转移优化为\(n log n\),(不选直接从i+siz[i]+1的位置转移否则从i+1转移)
单调队列优化多重背包...我觉得写不出来,到时候写二进制拆分吧。
凸优化
- 有\(k\)的限制,当不考虑\(k\)限制时的dp比较好写,打表发现dp数组斜率单调
可以二分一个权值,当选择一个数的时候加上该权值。这个时候可能还满足其他的单调性,也可以打表找一下[SDOI2016]征途
\(g[N]\)表示选了几个,更新答案的时候只需要在一定可以取到的点更新即可
while(l < r) {
LL mid = l + r >> 1;
check(mid);
if(g[N] > M) l = mid + 1;
else r = mid, ans = M * (f[N] - mid * M) - sqr(s[N]);
}
虚树
- 有效的点不会太多
考虑得到了询问点,如何构造出一棵虚树。
首先我们要先对整棵树dfs一遍,求出他们的dfs序,然后对每个节点以dfs序为关键字从小到大排序
同时维护一个栈,表示从根到栈顶元素这条链
假设当前要加入的节点为\(p\),栈顶元素为\(x = s[top]\),\(lca\)为他们的最近公共祖先
因为我们是按照dfs序遍历,因此\(lca\)不可能是\(p\)
那么现在会有两种情况
\(lca\)是\(x\),直接将\(p\)入栈。 设栈顶元素为\(x\),第二个元素为\(y\) 考虑把式子化成\(y = k x + b\)的形式 可以先用\(j > k\)且\(j\)比\(k\)优推出式子然后再考虑斜率/坐标是否单调。。 斜率单调:直接维护指针扫 斜率不单调:二分凸包 加入坐标单调:单调队列 加入坐标不单调:cdq 卷积形式\(f[i] = \sum_{j=1}^i {f[j] * f[i - j]}\) 对于要统计\(i - j\)的贡献的可以考虑翻转其中一个的下标变为\(i + (N - j)\) 含有通配符的字符串匹配问题可以构造多项式 基本不会,考到算倒霉。 如果有减法操作可以维护最大最小值,当除以一个数对于最大最小值产生的影响相同时改为减法运算 复杂度\(O(n \log^2 n)\) 维护区间\(and\)区间\(or\),若\(and\)上一个对两个标记的影响是相同的就改为区间加法(直接下传标记即可)。 记录区间最小值/次小值 若最小值\(\geqslant x\)则返回,否则若\(x\)大于最小值但小于次小值那么只考虑次小值的影响,否则暴力递归 复杂度\(O(n \log^2 n)\) 每个点上加入一个直线,可以直接标记永久化,每次 下放较短的一段。 复杂度\(O(n \log^2 n)\) 主席树在某些情况下可以支持区间加1,这时候只需要维护区间值/被加的次数标记永久化即可 从一个点到根的重链不会超过\(\log n\)条,所以有时候可以暴力找出来搞事情。 空间开不下? \(tan 90^o\) 这东西一般离线做比较稳,也就是从下往上合并的过程中就把询问都处理掉 否则每次需要新建一个节点 考到认倒霉。。 记\(A\)为无向图的度数矩阵(A[i][i]表示\(i\)号点的度数),\(D\)为无向图的邻接矩阵\(D[i][j]\)表示\(i\)与\(j\)的边数 记\(G = A - D\),那么图的所有不同的生成树等于任意一个\(n - 1\)阶主子式的行列式的绝对值 求行列式可以直接高斯消元后把主对角线上的所有元素乘起来复杂度\(O(n^3)\) 求\(a^x \equiv N \pmod P\)的最小的\(x\) \(a^{i * k - j} \equiv N \pmod P\) \(a^{i * k} \equiv N * a^j \pmod P\) 可以分为\(\sqrt{P}\)块暴力hash 当\(gcd(a, P) \not = 1\)时无解 \(f(n) = \frac{C_{2n}^n}{n + 1}\) \(D(n) = (n - 1)(D(n - 2) + D(n - 1))\) \(C_n^m \% P = C(n \% p, m \% p) * C(\frac{n}{p}, \frac{m}{p})\) 如果我们要求\(\sum \frac{A_i}{B_i}\)最大 可以二分答案,我们要求\(\sum \frac{A_i}{B_i} \geqslant K\) \[\sum{A_i} - K(\sum B_i) \geqslant 0\] 也就是每选一个会产生\(A_i - K B_i\)的贡献 设\((a, b)\)为第一棵树中的直径端点,\((x, y)\)为第二棵树中的直径端点。 将这两棵树合并后的树的直径一定是\((a, x) (a, y) (b, x) (b, y)\)中的一个
\(x,p\)分别位于\(lca\)的两棵子树中,此时\(x\)这棵子树已经遍历完毕,(如果没有,即x的子树中还有一个未加入的点y,但是dfn[y]
void insert(int x) {
if(top == 1) {st[++top] = x; return ;}
int lca = HLP::LCA(x, st[top]);
if(lca == st[top]) {st[++top] = x; return ;}
while(top > 1 && dfn[st[top - 1]] >= dfn[lca]) AE(st[top - 1], st[top]), top--;
if(lca != st[top])
AE(lca, st[top]), st[top] = lca;
st[++top] = x;
}
sort(p + 1, p + K + 1, comp);
for(int i = 1; i <= K; i++) {
if(p[i] != 1) insert(p[i]);
siz2[p[i]] = 1;
}
while(top > 1) AE(st[top - 1], st[top]), top--;
斜率优化
其他套路
多项式
FFT
FWT
拉格朗日插值
数据结构
平衡树
势能均摊线段树
超哥线段树
主席树
树链剖分
二维树状数组
unordered_map
线段树合并
计算几何
数学
Polya定理
MatrixTree定理
BSGS
Catalan数
错排问题
卢卡斯定理
斯特灵数
杜教筛
扩展欧拉定理
MillerRabin
反演
其他
01分数规划
树的直径