Tricks(长期更新)

记得每天总结时把题粘过来。匹配不上的,要么是水题,要么应当有新 trick。

船新版本的 trick 库

图论

具有高度对称性的图

咕咕咕。

差分约束

满足构造题的限制

大概率就算看出来也写不了。

就是转化题意后发现每个位置上都会加减一些东西,还要满足结果在一个范围内,那么就列出不等式,尝试差分约束。

树论

\(dep_{LCA}\)的转化

\(dep_{LCA(x,y)}\),可以先对\(x\)到根的路径上每个点的权值\(+1\),然后查\(y\)到根路径上的权值之和。发现这样完全避开了求\(LCA\),可以对更多个数的情况做优化。

P4211 [LNOI2014] LCA

DP

关于单调性

大力分析单调性总是好的。

\(a_i\)严格单增\(\iff\)\(a_i-i\)单调不降。类似还有。

关于前缀和

前缀和优化很好,但是定义DP数组时别直接定义前缀和形式的东西。要计算答案时再计算前缀和就好,别急

P9753 [CSP-S 2023] 消消乐

关于字符串上的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\)的最大子段和。然后就有

\[\begin{cases} f_{i,0}=\max(f_{i-1,0}+a_i,a_i),f_{i,1}=f_{i-1,1}+a_i & a_i\ne k\\ f_{i,0}=-inf,f_{i,1}=\max(a_i,\max(f_{i-1,0}+a_i,f_{i-1,1}+a_i)) & a_i=k \end{cases} \]

\(O(n)\)DP就好。

求两段的和最大

将两边的最大子段和加起来就行。

就是求出最大子段和后拼起来。

在转移的很多段里面DP的答案不变

把会使答案改变(或者说是会对答案产生贡献/影响)的点单独取出来跑DP。

莫名感觉像虚树(?

可能只是常数优化,但是常数有时也很重要。(不得不提某场校测(2024.10.21 T2)里\(676\)的常数导致\(O(n)\)过不了。)

定义域和值域互换

类似反函数。就是要求的一个东西值域很小而限制的值域很大,状态数太多了或者不好转,就上反函数。

比较冷门,但很牛。

CF721C

求从\(1\)出发,经过的边权和不超过\(k\),最多经过几个点。

发现要是直接把边权扔到状态里就爆了。

于是换一下,设\(f_{i,v}\)表示走到\(i\),经过点数为\(v\)时的最小边权和。

然后就可以转了,状态数是\(O(n^2)\)的,再加上拓扑排序和转移,总的也是\(O(n^2)\)的。

给状态加入更多限制

不好转,就多加限制。

这不一定体现在状态维数的增加,也可以是在状态之外钦定一些东西。

P11233 [CSP-S 2024] 染色(官方数据)

考场上定义了很多种状态,例如考虑\(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\)

观察值域后也可以考虑将值压到下标上。

P11233 [CSP-S 2024] 染色(官方数据)

还是这道题。

\(f_i\)表示考虑了前\(i\)位,钦定第\(i\)位与第\(i-1\)位颜色不同的最大分数。于是可以写出转移:

\[f_i=\max_{1\le j<i} \Big\{f_j+w(j+1,i-1)+a_i[a_i=a_{j-1}] \Big\} \]

其中\([j,i-1]\)是同色段,\(w(j+1,i-1)\)\([j+1,i-1]\)产生的贡献。

我们记\(s_i\)表示\([1,i]\)同色时产生的贡献,于是\(w\)可以差分出来。转移方程可以写成:

\[\begin{aligned} f_i&=\max_{1\le j<i} \Big \{f_j+s_{i-1}-s_j+a_i[a_i=a_{j-1}] \Big\}\\ &=\max_{1\le j<i} \Big \{f_j-s_j+a_i[a_i=a_{j-1}] \Big\}+s_{i-1} \end{aligned} \]

我们本来希望尝试记录前缀最大值直接优化,但是发现\(\max\)中套着艾弗森括号,搞不定。

观察一下,我们发现至少前两项是放在一起的,记\(pr_i=f_i-s_i\)

再想想,艾弗森括号成立时,对于这个式子,值肯定比不成立时更大。于是可以对于成立和不成立两种情况分别取\(\max\),再合到一起取\(\max\)

写成式子:

\[f_i=\max \Big \{\max_{1\le j<i} pr_j,\max_{1\le j<i\land a_i=a_{j-1}} pr_j+a_i \Big\}+s_{i-1} \]

然后前面的那一项就是\(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)\)

压缩状态,但不是状压

状态里可以相互推导/具有某种特定联系的,可以尝试去掉其中几个维度。

[ARC073F] Many Moves

第一直觉是定义\(f_{i,x,y}\)表示回答完\(i\)个询问后,一个棋子在\(x\),一个棋子在\(y\)的最小用时。然后发现状态数太多了,哪怕第一维可以滚掉。

继续观察一下,发现回答完第\(i\)次询问后,一个棋子一定在\(x_i\),另一个棋子不知道。于是修改状态为\(f_{i,j}\)表示回答完第\(i\)个询问后,一个棋子在\(x_i\),另一个棋子在\(j\)的最小用时。

考虑转移:

\[f_{i,x_{i-1}}=f_{i-1,j}+|j-x_i| \]

\[f_{i,j}=f_{i-1,j}+|x_i-x_{i-1}| \]

发现有绝对值,但其实对\(j\)的大小分讨就可以拆掉绝对值。根据拆掉后的符号不同,需要维护\(f_{i,j}+j,f_{i,j}-j\)

状态数还是很大,但是还好。第一种转移只对于一个位置改,第二种转移是对所有状态加上常数。于是上线段树,维护全局加,单点修,单点查就好。

矩阵优化

直观地可以看到状态中有一维范围很大,于是把这一维放到矩阵中递推,然后上快速幂。同时可能有多次询问。

状态数/转移数的优化

手玩一下发现转移其实很少/状态其实很少,就都存下来,再去跑。

关于排列的定义域和值域

其实会发现这两个东西很模糊,因为都是从\(1\)\(n\)的。在原来的序列上想不动的时候就可以转化题意放到值域上想(给每个值分配一个下标之类的)。

转移过程中会遇到特殊点/特殊限制

在特殊点处停下来,特殊转移一下,再继续跑。

二分

中位数

二分一个\(mid\),大于等于它的数换成\(1\),小于它的数换成\(-1\),查询和是否大于等于\(0\)

二分hash

同字符串。什么字符串技巧都不会的时候,会这个就够了。

字符串

比较两个字符串的大小

二分+hash求LCP,然后比较后面那个字符,是\(O(\log n)\)的。

结合倍增

跳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\)的因子。

数论相关

余数找环

注意到\(a^{\varphi(m)}\equiv 1\pmod m\)(\(a\bot m\)),那么当\(a\bot m\)时,环长一定是\(\varphi(m)\)的一个因数。然后枚举去找。(来自2024.9.30数学测试)

和式相关

最大化一段前缀与一段子段的和

最大化\(\sum\limits_{i=1}^q a_i+\sum\limits_{k=s}^t a_k\),其中\(q,s,t\)是待定的参数。

有个结论:\([1,q]\)\([s,t]\)要么包含,要么无交。

可以反证,设\(s\le q<t\),那么由于最大化了\(\sum\limits_{i=1}^q a_i\),可知区间\([q+1,t]\)的和非正,于是\(t=q\)时一定不比此刻更劣,所以得证。

并且还可以加强一下,当包含的时候,一定有\(t=q\),这是显然的。因为若\(t<q\),当\([t+1,q]\)的和小于\(0\)时,\(q\)的最大性不成立,而当\([t+1,q]\)的和大于等于\(0\)时,使\(t=q\)一定不会更劣。得证。

在一次比赛中(2024.11.25),出题人加上了需要保证\(s>q\)或者\(t\le q\)的限制,但由于以上结论,这个限制是废的。

分治

求关于每个区间的信息

发现这种都可以考虑分治。是很好的思考方向。

求出每个区间的最大前缀和之和

上接最大化一段前缀和一段子段的和。

要求出每个区间的最大前缀的和。这里前缀可以取一段空前缀,即一个区间的最大前缀和可以为\(0\)

记前缀和\(s_i=\sum\limits_{k=1}^i a_k\),考虑枚举一下左端点\(l\),那么对于一个右端点,这段区间的贡献就是\(\max\limits_{k=l}^r s_k-s_{l-1}\)

从大到小枚举\(l\),同时维护一个单调栈\(T\)\(T\)内部从栈顶到栈底满足\(s_{T_i}\)单调递增,\(T_i\)存的是原数组的下标。

那么对于一个确定的\(l\),我们要统计所有\(r\ge l\)的区间的贡献。考虑二分出第一个\(T_i\)使得\(s_{T_i}\ge s_{l-1}\),这样在\(T_i\)之前的\(r\)是没有贡献的。对于一个位置\(T_i\),右端点落在\([T_i,T_{i-1})\)中的区间的贡献为\(s_{T_i}\cdot (T_{i-1}-T_i)-s_{l-1}\cdot(T_{i-1}-T_i)\)。那么总的贡献为\(\sum\limits_{j=1}^i s_{T_j}\cdot (T_j-T_{j-1})-s_{l-1}\cdot (n-T_i+1)\)。移动\(l\)时,\(T\)中的贡献只有弹出去的部分和加入的一个点的部分会改变。记一下栈中的前缀和,每次加入一个点只需更新一处。统计贡献就二分一下,然后直接查。总的是\(O(n\log n)\)

求出每个区间的最大子段和之和

上接最大化一段前缀和一段子段的和。

以及上接求出每个区间的最大前缀的和。

发现单调栈的做法不太好延续过来,遂放弃。

考虑分治(因为区间的最大子段和就是考虑分治)。现已分治到\([L,R]\),再分治到\([L,mid]\)\([mid+1,R]\)。现在要算的就是左端点\(l\)\([L,mid]\),右端点在\([mid+1,R]\)的区间的最大子段和之和。

考虑对于每个在\([L,mid]\)中的\(i\),求出\([i,mid]\)的最大子段和\(f_i\)以及最大后缀和\(g_i\);对于每个在\([mid+1,R]\)中的\(i\),求出\([mid+1,i]\)的最大子段和\(f_i\)以及最大前缀和\(g_i\)

考虑从\(mid\)\(L\)\(l\),对每个\(l\)计算贡献。设右端点为\(r\),则\([l,r]\)的贡献为\(\max\{f_l,f_r,g_l+g_r\}\)。可以注意到\(r\)递增,\(l\)不变时\(f_r\)\(g_l+g_r\)单调不降,\(l\)继续向左扫时\(f_l\)单调不降且可以证明\(f_l-g_l\)仍然大于\(g_r\)\(f_l>g_l+g_r\)(就是下面的结论),于是可以用一个指针扫出\(f_l>\max\{f_r,g_l+g_r\}\)\(r\)所在的前缀。这个的贡献直接乘一下就好了。

然后考虑\(g_l+g_r>f_r\)的做法。移项得到\(f_r-g_r<g_l\),发现\(f-g\)是单调不降的。有单调性之后又可以双指针一下,就好做了。现在来证明这个单调性。

\(g_i\)视作最大前缀和,它的端点为\(k\),它后缀和时证明是相同的。由最大化一段前缀和一段子段的和的结论,\(f_i\)的端点\(l\)\(r\)只有两种情况:要么\(r=k\),要么\(l>k\)

现在考虑在已经处理出\(f_i\)\(g_i\)的序列后加入一个数字\(a_{i+1}\)

考虑第一种\(r=k\)时,那么如果此时最大前缀和变化,则必然是所有数的和大于\(g_i\),于是\([k+1,i+1]\)的和大于\(0\),此时最大子段和也一定会加上\([k+1,i+1]\)这一段。于是最大前缀和与最大子段和的变化量相同。\(f\)的变化量不小于\(g\)的变化量。

考虑第二种\(l>k\)时,如果此时最大前缀和变化,则同上,\([k+1,i+1]\)的和大于\(0\)。而由于\(l>k\),所以\([k+1,r]\)的和是小于等于\(0\)的。于是\([r+1,i+1]\)的和大于\([k+1,i+1]\)的和,于是\(f\)的变化量不小于\(g\)的变化量。

综上,\(f-g\)单调不降。

挂个原题:Treasure Hunt

DS

区间加区间乘积

经典不可做题。所以必定有其它性质(比如说带取模,然后加的东西累乘起来会被模成 \(0\),所以本质问的是后面那个问题),一般是问区间选 \(c\) 个数的乘积之和。

这个东西就区间维护 \(sum_i\) 表示区间中选 \(i\) 个数相乘的和。然后区间加后可以暴力卷积维护这个东西。

根号

思维题练习

脑子痒痒的

posted @ 2024-10-03 21:35  RandomShuffle  阅读(61)  评论(1)    收藏  举报