template-DP
单调队列
O(n)求 最大值-最小值 <= k的子序列个数
1 | int getNum(int target) |
单调栈
求全为1的最大子矩形面积
1 | int cal() { |
基础
最长上升子序列
nlogn做法
二分
\(f[i]\)表示长度为\(i\)的最长上升子序列中, 最小的末尾数值. \(siz\)为最长上升子序列长度.
1 | f[1] = raw[1]; |
悬线法
同样用来求最大子矩形问题, 时间复杂度\(O(nm)\)
设\(l[x][y],r[x][y],u[x][y]\)为点\((x,y)\)可向左/右/上移动的最大距离. (其中向上扩展的最大距离即为"悬线"长度)
求出以上距离后, 再令\(L[x][y],R[x][y]\)为\((x,y)\)的悬线可以向左/右扩展的最大距离. 有: \[ L[x][y] = \begin{cases} \min(l[x-1][y],L[x][y]) & (x-1,y)合法\\ l[x][y] & (x-1,y)非法 \end{cases} \] 再统计\(\max(u[x][y] \cdot (L[x][y] + R[x][y] - 1))\)即为答案.
状压dp
TSP问题
(变体):给出一个带权完全无向图, 求恰好经过每一点的最短简单路径
1 | int f[1<<20][20]; |
求无向图简单环个数
设\(f[S][i]\)表示状态为\(S\), 终点是\((1<<i)\), 起点是\(lowbit(S)\)的链的个数:
由于是无向图, 需要减去边的个数后除以2.
1 | int ans = 0; |
数位dp
感觉这个神秘板子部分就数位dp有用啊?
例题1
定义一个正整数的价值是把这个数的十进制写出来之后,最长的等差子串的长度。
求\([l,r]\)范围内数字价值的总和。
1 |
|
例题2
求不含前导零且相邻两个数字之差至少为\(2\)的正整数
1 |
|
优化
斜率优化
维护\(y_i = k_i x + b_i\)的最小值. update(\(k,b\))需要保证斜率\(k\)单调递减. 其他情况自己推一下即可.
原理如下图. 插入时, line
存构成当前下凸壳的直线, point[i]
代表直线line[i]
和line[i-1]
的交点横坐标. 新加入一条直线(\(l_3)\)时, 设\(l_3\)和\(l_2\)交于\(B\), \(l_2\)和\(l_1\)交于\(A\). 若\(B.x < A.x\)则显然\(l_2\)不再组成下凸壳, 将其删去. 使用单调栈维护这一过程即可. 查询时在point
上二分即可找到对应直线.
1 | const ll INF = 1e18;// |