Tricks(长期更新)
会很杂,尽量分类,每个trick会配题。
难以分类的
难以分类可能只是自己太菜了。
一类断开/分割/etc.相关的问题
考虑时光倒流,改成合并,然后大概率变得好做好想了。
在很多题中都有出现,如DP,贪心,图论等。
曼哈顿距离与切比雪夫距离的转化
对于两点\((x_1,y_1),(x_2,y_2)\),曼哈顿距离为\(|x_1-x_2|+|y_1+y_2|\),切比雪夫距离为\(\max(|x_1-x_2|,|y_1-y_2|)\)。
画图可以发现到原点的曼哈顿距离为\(1\)的点形成一个对角线在坐标轴上的正方形,切比雪夫距离为\(1\)的点形成一个边与坐标轴平行的正方形。且后一个正方形的边长为前一个的\(\sqrt 2\)倍。
我们可以想到二者可以相互转化。
大力推一下:
容易发现这就是\((x_1+y_1,x_1-y_1),(x_2+y_2,x_2-y_2)\)两点之间的切比雪夫距离。
懒得敲完了自己看看吧
容易发现这就是\((\frac{x_1+y_1}{2},\frac{x_1-y_1}{2}),(\frac{x_2+y_2}{2},\frac{x_2-y_2}{2})\)之间的曼哈顿距离。
以上两种转化也可以视作旋转了坐标轴。
给出序列\(d\),\(d_i\)对应着四种操作之一:
-
从\((x,y)\)到\((x+d_i,y)\)
-
从\((x,y)\)到\((x-d_i,y)\)
-
从\((x,y)\)到\((x,y+d_i)\)
-
从\((x,y)\)到\((x,y-d_i)\)
问能否从\((0,0)\)走到\((A,B)\)
考虑换系,将\((x,y)\)转化为\((x+y,x-y)\),可以发现\(x\)和\(y\)上的移动相互独立了,每种\(x\pm d_i,y\pm d_i\)的操作都对应原来唯一确定的一种方案。于是对两维分别跑背包即可。
图论
生成树
有向图中的内向/外向、最小/最大生成树
别太死板了以为只有无向图最小/最大生成树。
有向图中的内向/外向、最大/最小生成树是类似的。
具有高度对称性的图
咕咕咕。
恰好选\(k\)条满足某种限制的边,求最小生成树
似乎可以理解成wqs二分,但我不会。
可以考虑调整边权,比如给一些满足某种限制的边的边权加上一定的偏移量,于是使得一些本来选不了的边可以选了。
然后注意到偏移量有单调性,二分启动。
恰好选\(need\)条边,考虑给每条白边的边权加上二分出来的偏移量\(k\),二分出选到的白边数量\(cnt\ge need\)时,由于保证有解,多出来的白边可以视作边权相等的黑边(注意这是一定存在的),所以最后将最小生成树权值\(val-cnt\times k\)即可。小心二分写挂。
差分约束
限定区间内选点个数
给出限定\([l_i,r_i]\)中至少选\(c_i\)个点,求一个可行的选点方案。
Sol:
记前缀和\(sum_i\)表示前缀中有多少个点选了,于是有\(sum_r-sum_{l-1}\ge c\)。
同时,要保证每个点要么选要么不选,于是有\(1\ge sum_i-sum_{i-1}\ge 0\)。
跑差分约束即可,每个点的状态即\(sum_i-sum_{i-1}\)。
满足构造题的限制
大概率就算看出来也写不了。
就是转化题意后发现每个位置上都会加减一些东西,还要满足结果在一个范围内,那么就列出不等式,尝试差分约束。
树论
对\(dep_{LCA}\)的转化
求\(dep_{LCA(x,y)}\),可以先对\(x\)到根的路径上每个点的权值\(+1\),然后查\(y\)到根路径上的权值之和。发现这样完全避开了求\(LCA\),可以对更多个数的情况做优化。
DP
关于前缀和
前缀和优化很好,但是定义DP数组时别直接定义前缀和形式的东西。要计算答案时再计算前缀和就好,别急。
关于字符串上的DP
或许可以当成字符串题做,用一些字符串算法(或者只是思想)结合DP。
长度至少为\(k\)的最大子段和
trivial的问题,先钦定一段长度为\(k\)的子段要选,然后正常求\(f_i\)表示\([1,i]\)中钦定选第\(i\)个数的最大子段和,然后枚举每个长度为\(k\)的区间\([l,l+k-1]\),求\(\min(sum+f_{l-1})\)就好。
钦定必须选某个数\(k\)的最大子段和
稍微改动了一点,定义\(f_{i,0}\)表示没选\(k\)的最大子段和,\(f_{i,1}\)表示选了\(k\)的最大子段和。然后就有
\(O(n)\)DP就好。
求两段的和最大
将两边的最大子段和加起来就行。
就是求出最大子段和后拼起来。
在转移的很多段里面DP的答案不变
把会使答案改变(或者说是会对答案产生贡献/影响)的点单独取出来跑DP。
莫名感觉像虚树(?
可能只是常数优化,但是常数有时也很重要。(不得不提某场校测(2024.10.21 T2)里\(676\)的常数导致\(O(n)\)过不了。)
定义域和值域互换
类似反函数。就是要求的一个东西值域很小而限制的值域很大,状态数太多了或者不好转,就上反函数。
比较冷门,但很牛。
求从\(1\)出发,经过的边权和不超过\(k\),最多经过几个点。
发现要是直接把边权扔到状态里就爆了。
于是换一下,设\(f_{i,v}\)表示走到\(i\),经过点数为\(v\)时的最小边权和。
然后就可以转了,状态数是\(O(n^2)\)的,再加上拓扑排序和转移,总的也是\(O(n^2)\)的。
给状态加入更多限制
不好转,就多加限制。
这不一定体现在状态维数的增加,也可以是在状态之外钦定一些东西。
考场上定义了很多种状态,例如考虑\(f_{i,0/1}\)表示考虑前\(i\)个,第\(i\)个填红/蓝的最大得分。但是发现这个状态很无力,对转移很没有帮助。
考虑给状态加入在维度之外的限制。定义\(f_i\)表示考虑了前\(i\)个,钦定第\(i\)个与第\(i-1\)个颜色不同的最大得分。
首先答案是\(f_{n+1}\),因为加入了钦定,而第\(n+1\)个本来就和第\(n\)个不同,且第\(n+1\)个无论怎么选贡献都是\(0\),所以是正确的。
然后考虑第\(i\)个前面第一个和它颜色相同的位置是\(j\),于是发现在原来的状态中\([j+1,i-1]\)的贡献难算的问题(不好确定\(j+1\)的贡献)解决了:\(j+1\)的贡献在\(f_{j+1}\)中已经解决了,而\([j+2,i-1]\)的贡献是好算的。于是转得动了。
搞掉艾弗森括号
需要分析一点有艾弗森括号的式子的性质,然后化简为繁,再化繁为简。
后面化来化去的或许可以考虑将一坨只带一个真实的变量的式子用一个东西表示,如下文\(mx\)。
观察值域后也可以考虑将值压到下标上。
还是这道题。
设\(f_i\)表示考虑了前\(i\)位,钦定第\(i\)位与第\(i-1\)位颜色不同的最大分数。于是可以写出转移:
其中\([j,i-1]\)是同色段,\(w(j+1,i-1)\)是\([j+1,i-1]\)产生的贡献。
我们记\(s_i\)表示\([1,i]\)同色时产生的贡献,于是\(w\)可以差分出来。转移方程可以写成:
我们本来希望尝试记录前缀最大值直接优化,但是发现\(\max\)中套着艾弗森括号,搞不定。
观察一下,我们发现至少前两项是放在一起的,记\(pr_i=f_i-s_i\)。
再想想,艾弗森括号成立时,对于这个式子,值肯定比不成立时更大。于是可以对于成立和不成立两种情况分别取\(\max\),再合到一起取\(\max\)。
写成式子:
然后前面的那一项就是\(pr\)的前缀最大值,拿变量\(prmx\)记一下就好了。
对于后面那一个,考虑记\(mx_x=\max_{1\le j<i\land x=a_{j-1}} pr_j+x\)。这个东西在每次\(i\)往右移之前都要更新,具体的,式子中取\(j=i\)时,\(mx_{a_{i-1}}=\max\{mx_{a_{i-1}},pr_i+a_{i-1}\}\)。
最后,转移就变成了\(f_i=\max\Big \{prmx,mx_{a_i}\Big\}+s_{i-1}\)。
以上的转移过程都是\(O(1)\)的。状态数\(O(n)\),于是总的是\(O(n)\)。
压缩状态
状态里可以相互推导/具有某种特定联系的,可以尝试去掉其中几个维度。
第一直觉是定义\(f_{i,x,y}\)表示回答完\(i\)个询问后,一个棋子在\(x\),一个棋子在\(y\)的最小用时。然后发现状态数太多了,哪怕第一维可以滚掉。
继续观察一下,发现回答完第\(i\)次询问后,一个棋子一定在\(x_i\),另一个棋子不知道。于是修改状态为\(f_{i,j}\)表示回答完第\(i\)个询问后,一个棋子在\(x_i\),另一个棋子在\(j\)的最小用时。
考虑转移:
发现有绝对值,但其实对\(j\)的大小分讨就可以拆掉绝对值。根据拆掉后的符号不同,需要维护\(f_{i,j}+j,f_{i,j}-j\)。
状态数还是很大,但是还好。第一种转移只对于一个位置改,第二种转移是对所有状态加上常数。于是上线段树,维护全局加,单点修,单点查就好。
二分
中位数
二分一个\(mid\),大于等于它的数换成\(1\),小于它的数换成\(-1\),查询和是否大于等于\(0\)。
二分hash
同字符串。什么字符串技巧都不会的时候,会这个就够了。
字符串
比较两个字符串的大小
二分+hash求LCP,然后比较后面那个字符,是\(O(\log n)\)的。
感觉信息很多的情况
字符串特有的。
发现自己要维护的信息是\(n^2\)甚至更高级别的,就要尝试能否利用之前求出的信息来递推接下来的信息/压缩接下来的信息。
字符串总是会有点DP/递推的想法在里面。
结合倍增
跳border的时候可能会跳很多次,用倍增的思想可以优化至\(\log\)。
如用类似ST表一样的思路,定义\(st_{i,j}\)表示从\(i\)开始跳\(2^j\)次border可以到的位置,然后就可以倍增起来。
对KMP的改造
有时可能要求最短border。考虑在KMP的过程中递推这个东西。
设\(g_i\)表示\([1,i]\)的最短border,若为\(0\),将\(g_i\)设为\(i\),这样可能会方便解决问题。
在\(i\)处我们求出\(fail_i=j\)后,进行讨论:
-
\(j>0\)时,若\(fail_j>0\),则\(g_i=g_{fail_j}\);若\(fail_j=0\),则\(g_i=j\)。
-
\(j=0\)时,\(g_i=i\)。
特别的,\(fail_1=0\),\(g_1=1\)。
构造/Ad-hoc
关于图的形态的构造
多尝试特殊形态的图,记录一下:
-
链
-
菊花图
-
树
-
链套菊花
-
基环树
-
网格图
(2024.10.23 T1就是先考虑链,然后对链调整,改造成链套菊花。)
数学
发现联考T1都放聪明的数学题,感觉需要记录一下。
二进制相关
二进制下找因子
听上去就很扯淡。
但是这是真的。
发现\(lowbit(x)\)一定是\(x\)的因子。