补题集合

数论,dp

思路:

令A为一个正整数序列,\(a_{i}\)为序列中的数

  1. \(a_{i}\)分解质因数,\(a_{i}\) = \(p_{1}^{x1}\) * \(p_{2}^{x2}\)...
  2. 则LCM(A)就是由每一个p的指数取max组成的
    如果子序列合法,那么子序列中每个数分解质因数之后,至少有一个数的p的指数和M的相同。然后我们将相同记为1,那么就将题目转化成从n个数中选一些数使得他们的异或值为Target的方案数,因为数组开不下所以用滚动数组优化,

概率dp,记忆化搜索

思路:

由于操作二是一个概率性问题,所以定义dp[n] 为让n变为0的最小期望花费,每次都是操作二选一所以不难推出如下状态转换

  1. 如果选择操作一:dp[n] = dp[n / a] + x;
  2. 如果选择操作二:dp[n] = E(X) + y; E(X)表示骰子要出来之后的平均期望。

trick:

  1. 因为n的范围在\(10^{18}\),直接开数字组会爆,所以选择map加dfs记忆化搜索
  2. 代码实现过程中,发现如果骰子摇出来的是1,会导致死递归(n一直不变),所以在算E(X)的时候单独把1拎出来

换根dp,典题

思路:

如果是固定根节点的话只需要dfs一下整棵树就可以在\(O(n)\)的时间内求出答案,但是这题的根节点不固定所以要额外枚举一次根节点,总的算下来复杂度就是\(O(n^{2})\)

  1. 假设当前根节点为u,那么从父节点fa转到u,答案的变化就是 ans[u] = ans[fa] - A(以u为根节点的子树中c的和) + B(除了A以外的c的和) = ans[fa] + 总和 - 以u为根节点的子树
  2. 所以先预处理出来c的总和sum 和 siz[i]: 以i为根节点的子树中c的和
  3. 然后利用1中的状态转移进行dp,初始化就是dp[1], dp[1]可以写dfs搜一下

trick:

证明1 : 根从fa -> u的话,发现以u为根节点的子树中所有节点到根的距离减少一,意味着相对ans[fa]减少了siz[u],但是其它点到根的距离增加一,所以要再加上这部分的和
类似题
类似题
类似题
类似题

切尔雪夫转曼哈顿,快速算区间内两两点的曼哈顿距离之和

思路

题目给的是每次可以沿着对角线移动,这样算起来很不方便因为答案可能带根号而且复杂度\(O(n^{2})\),所以要先把坐标转成我们熟悉的曼哈顿距离,只需要把坐标轴旋转45°就好了
假设当前坐标为\([x, y]\)

\[\left[\begin{array}{c} x & y \end{array}\right] * \left[\begin{array}{c} cosx & -sinx \\ sinx & cosx \end{array}\right] \]

这样求出来坐标为\([\sqrt{2} / 2 * x + \sqrt{2} / 2 * y, \sqrt{2} / 2 * x - \sqrt{2} / 2 * y]\),这样计算也不方便所以再乘个\(\sqrt{2}\)就成了[x + y, x - y],这样就得到了在曼哈顿坐标下的点,那么原来向对角线方向移动一个单位就变成了竖直或水平移动\(\sqrt{2}\)个单位,我们再乘个\(\sqrt{2}\)就变成移动两个单位,因为一次只能移动两个单位所以只能奇偶性相同的点能互相到达,所以奇偶分开算。曼哈顿距离的x, y也是独立的所以也分开算,这样就只需要排序加前缀和就可以\(O(n)\)算出答案,但是上面一共乘了两个\(\sqrt{2}\)所以答案要除以2,在计算过程中因为每对点只算一遍所以可以先排序然后维护一个前缀和就能在\(O(n)\)内算出

回文长度,思维

思路

由题意易得\(当i \in \{奇数\}\)时,对应位置肯定是字符。所以赛时就想从这找突破点但其实很难,因为当前位置放'a' or 'b'会对前后的a[i]产生影响。不妨换个角度,考虑每个'|'的位置,不难发现如果a['|'] = 1那么就说明前后字符发生了变化,且第一个字符我们一开始就可以直接选定,所以就有了一个递推关系直接枚举就行。

数论,扩展欧几里得

思路

计算几何,求面积技巧

思路:

先定义一下什么是凸多边形,就是把多边形一条边无限延申后,整个图像只在直线的一边出现。由此本题要求的最小的凸多边形就是三角形,数据范围很小所以直接暴力枚举三个点。问题关键在于如何快速求三角形的面积,因为本题只给出了点的信息,所以可以利用叉积的方式求三角形面积,但是面积可能是浮点数,为了保证精度问题,所以我们最后再除以二

二分,思维

思路

通读题面后易得,某一条路径对应的答案其实就是路径外部的最小点,若某一答案为ans,则意味着[0, ans - 1]中所有的点都应该在路径上,思考发现答案具有二分性因为如果ans为答案则ans - 1肯定也是答案,因为[0, ans - 2]是[0, ans - 1]的子集。所以可以考虑二分答案,check函数是关键。因为每次只能向下或向右移动,所以路径上的点的横纵坐标都应该是单调递增,所以可以枚举其中一个然后判断另一个是不是单调递增的从而判断check是否合法

树上二分

思路

当我们断[u, fa]这条边断开时,只有u的祖宗节点才会受到影响而且离u越近越受到影响越大,所以由u的祖宗节点组成的死域点集合是存在二分性的,断掉这条边之后离u比较近的一些点祖宗死域点可能会突变成非死域点。所以可以dfs一下断开每条边所能解救的最多死域点,然后答案就是一条边不断的死域点个数减去这个maxx。

trick

要考虑回溯的情况。

思维,正难则反

思路

  1. 先按左端点排序,然后用线段树做区间加,区间查询最大。注意离散化
  2. 直接算相交区间是比较难的限制条件比较多,所以先算全部的然后再减去不相交的,不相交就只需满足l[i] > r[j]则两区间不相交,先分别对左右端点排序然后双指针扫一遍

状压DP,bfs

思路

把整个地图的鱼看成一个状态,每次放一个炸弹就会改变这个状态,然后就抽象出一个搜索树问题就变成了找根节点到0状态的最短路显然bfs可做。关键是怎么把地图存下来,可以观察到每个鱼塘最多只有三条鱼,而且最多只有十个鱼塘。所以可以把整个地图的鱼压缩成一个四进制数,形如init = a[0] * 1 + a[1] * 4 + a[2] * 16...,含义为第i个鱼塘有a[i]条鱼。转移的时候就暴力枚举在每个点放炸弹。

tirck

  1. 因为地图很大,但是鱼塘很少。所以不是每个点都有意义,只有鱼塘的上下左右以及自己这五个点放炸弹才有意义,所以只需要在这些点上进行转移
  2. 在转移时状态用十进制表示,枚举的时候要变回四进制来判断第i个鱼塘还有没有鱼,形如a[1]位置减少一条鱼那么对应十进制状态要减少4

推公式,经典套路

思路

  1. \(V_{n}\) = \(\sum_{i = 1}^n\)n * \(10^{(i - 1) * len}\),易得这是个等比数列求和公式,化简之后用快速幂就可以了

数位性质

思路

  1. 令 n = \(x_{1}\) * 10 + \(x_{2}\) 易得 k * n = k * \(x_{1}\) * 10 + k * \(x_{2}\), D(k * n) = k * \(x_{1}\) + k * \(x_{2}\)
  2. k * D(n) = k * \(x_{1}\) + k * \(x_{2}\)
  3. 由上式可知,k * \(x_{i}\)不能超过10,不然会发生进位导致结果不相等,所以每一位取值在[0, 9],所以当位数为r时可选数字为\((9 / k + 1)^{r}\)

计数,字典树

思路

直接考虑答案,如果答案是abc那就说明最多可选的字符串cnt = 所有以abc开头的 + abc*开头的各选一个,如果 cnt >= k那么说明答案就是abc。直接暴力枚举

trick

代码感觉比较难写,因为要集合的LCP最大,所以那些长度小于abc的也可以要选上

多源bfs

思路

当可忍受的辣度为\(w_{i}\)时,图中所有\(a_{i} <= w_{i}\)的点都将成为合法点,问题就变成了求从\(p_{i}\)点出发到合法点的最小距离,这是多目标点求最短路反过来可以转成多起点求最短路。然后就变成了熟悉的多源最短路问题。
所以可以开一个数组dp[i][j]来维护当阈值为ji到合法点的最短路,因为阈值最多不超过100所以只需要跑100遍bfs

trick

类似题还有二进制,bfs找连通块

  1. (u, v)是否存在一条路径的与值大于等于V
  2. (u, v)是否存在一条路径的与值等于V
    显然问题一可以通过多次求解问题二来解决,所以可以通过枚举[V, \(2_{60}\))次问题二来得到问题一的答案,显然问题二中可走的边一定满足v & va == va,在这个条件下整张图有些边就会变得无效,即图将变成若干个连通块。显然只有在一个连通块中的点才可以互相到达。但是直接枚举的话会超时,考虑&的性质发现,若a > b则a的二进制和b的二进制一定有一段相同的前缀,且第一个不一样的地方必须是a[i] = 1, b[i] = 0。所以枚举的时候每次都增加一个lowbit(i)即可。

总结

这类问题都有一个特征就是题面中都会存在一个阈值,随着阈值的改变图中的点或者边将会发生改变。在第一个题中随着\(w_{i}\)的改变有些点将从目标点变成起点,第二个题中随着阈值改变有些边将从合法边变成无效边

第二类斯特林数

思路

我们把m个数展开成二进制,可以发现每列只能有一个1,每一行可以有多个1。一共有n1也就是说有n列。等价于将n1放进m个盒子里面,不能有空盒子。直接套用第二类斯特林数

位运算技巧,枚举

思路

令最后的总重量为C,则 C < m,说明C 和 m有一段相同的前缀...,所以我们可以枚举把m中的哪一位1改成0。在这样的m下w[i]和m前缀必须相同,后缀随便取

trick

  1. 后缀随便取 => xxx1111 & xxxaaa = xxxaaa

割边,生成树

思路

为了让点对最少,肯定优先断割边。这样一个连通图就变成两个连通图,点对就减少了\(cnt_{A} * cnt_{B}\)个。所以我们只要枚举每条割边然后取max,问题在于如何求出割边两端的cnt,我们可以先将图转成树,然后dfs求出每棵子树的大小

trick

在枚举割边时,我们需要的是当前这条割边的儿子的子树大小。因为在生成树时是从小到大枚举,所以小的是父亲,大的是儿子

DP,bfs

思路

\(C_{ij} = a_{ij} * p * p...\)如果将\(C_{ij}\)看成这样的形式然后再去模p - 1,我们可以发现后面一部分模出来全是1,所以\(C_{ij}\) = \(a_{ij}\) % (p - 1),所以一条路径上的和就为(\(a_{ij}\) % (p - 1) + \(a_{xy}\) % (p - 1)) % (p - 1),所以定义\(DP_{ijk}\)为从起点到(i, j)且余数为k的最短路,然后可以用bfs跑一遍

有理数,进制,dfs

思路

\(\frac{p}{q}\)k进制下为有理数等价于\(\forall x 使得 p * k^{x} = a * q\),所以q一定是\(p * k^{x}\)的因数,所以将\(p * k^{x}\)分解质因数然后dfs

trick

由数据范围可知x的一定不会超过100,所以直接令x为100

记忆化搜索

思路

对于一条合法光路,路上每一点在第一次搜索时答案就已经确定了。所以每个点每个方向最多只会被搜一次。可以先预处理出来整个图的边界光线照射的答案,然后O(1)回答

trick

  1. 光路可能是环,所以dfs的退出条件有两个。
  2. 搜出的光线是正向的,但是答案要反着记。

换根DP

思路

很明显的换根DP,关键在于如何定义状态和状态转移,令DP[u]表示以u为根节点的子树中不合法的边的数量,那么u如果为合法方案则DP[son] = 0,并且edge(u, v)为合法边。我们考虑根从u->son,首先就有DP[u] -= DP[son] + (edge(u, v)),
DP[v] += DP[u] + (edfe(v, u))。

trick

在dping时要注意恢复现场,因为当转移到下一个儿子时,父亲要保持不变

对顶堆,中位数

思路

a[i + 1] - a[i] = (i + 1) - i,所以先把数组的每个数减去下标,问题就转化成了在最多k次操作下最长一段值相同的子数组。将区间变成相同的数的最小代价就是变成中位数。所以用对顶堆来维护区间中位数以及代价,然后用双指针来找最长区间

枚举

思路

首先想到暴力枚举x, y。但是这样复杂度O(\(n^{4}\))铁定炸了,如果能把\(\sum_{x}\)\(\sum_{y}\)的结果预处理出来,那问题就转化成了在两个集合中分别取一个数要求\(a + b <= D\),这个只要二分就可以了复杂度O(nlog(n))。
预处理\(\sum_{x}\)时,可以排序然后维护一个前缀和与后缀和。\(\sum_{y}\)也是一样的

期望,取模类使用

思路 -> 看题解

trick

对于函数f来说,我们按位来计算贡献值。比如\([0, a]\)中有x个数的第k位为0,\([0, b]\)中有y个数的第k位为0。那么这一位的贡献就是\(x * y * (1 << i)\)
快速计算x,y可以根据周期性来算,具体看代码。

数论,构造

思路

假设第一个位置是0,即\(a_{1} * b_{1} = 0 (%mod)\),也就是说存在\(u * v = n * m 且 u | a_{1} , v | b_{1}\)。那么第一行最多有\(\frac{n * m}{u}\),因为上限是\(n * m\),每一列都要乘个\(u\),所以理想情况就是把\(u\)的倍数逐个填上去。又因为只有\(m\)列,所以\(m \leq\) \(\frac{n * m}{u}\),所以\(u \leq n 且 v \geq m\)。同理去分析第一列会发现\(n \leq \frac{n * m}{v}\)
\(u \geq n 且 v \leq m\),综上\(u = n 且 v = m\)。说明只存在一对\((u,v)\)满足条件说明n和m互质。所以一个合理的构造方案就是把n和m的倍数逐个放上去。

trick

  1. 4 * 8 = 2 * 16 = 32。说明如果u和v不互质那么一定不止一对(u,v)
  2. 放到最后的倍数时,在模意义下正好是0。这会导致这一行全是0,所以可以第一位放个1,然后逐位加m

逆序对,思维

思路

对B操作等价对A进行操作,当一个数字交换到\(a_{i} = b_{i}\)时,那就不能再动这个位置了。所以将A操作多少次变成B的操作数的奇偶性就是答案。
我们可以先映射出\(a_{i}\)在B中的位置,再求位置序列的逆序对个数。逆序对个数等于操作数。

Dp

思路

如果只能沿着一个方向走的话,可以在\(O(n^{2})\)的复杂度内解决得到\(g[i][j]\),代表从i点出发时间为j的最大收益,如果可以回头走的话\(dp[i][j] = max(g[i][j],dp[i - 1][j - 1], dp[i +1][j + 1])\)

trick

在dp的时候要先枚举时间再枚举位置,因为如果先枚举位置会导致\(dp[i][j]\)会使用到还没求解的\(dp[i + 1][j - 1]\).

动态维护树的直径,贪心

思路

如果树只有一条链,那么辟谣的起点肯定选择这条链的中点。当树不是链时,必须把谣言树的直径完全辟谣才行,可以继续选择树直径的中点辟谣点。当\(2 * k * (t - t_{0}) \geq 直径\)时,此时的\(t\)就是答案。观察不等式可以发现当\(k\)变小时\(t\)变大,所以可以双指针求解答案。还需要预处理出来每一时刻谣言树的直径。

tirck

  1. \(t\)的上限是\(2 * n\),当谣言树和辟谣树的根重合时且\(k = 1\)
  2. 如果当前树的直径为\((u, v)\),加入一个点\(x\)。则新的直径应该在\((u, x), (v, x), (u, v)\)中选最大的。当\(t+=1\)时,会有一层结点加入谣言树。所以可以对这一层的结点都取\(max\)

思维

思路

如果直接按题目意思模拟的话,太麻烦了需要维护一个优先队列。经观察可以发现每次合成一定是一个奇数一个偶数合成得到一个奇数,所以其实每个数最多只会被用来合成一次。答案一定是\(a_[i]和2 * a_{i} + 1,2 * a_{i} - 1\)里面挑。所以先把这些数放一起然后贪心从大到小检查每个数能不能由合成得到。

trick

  1. 在检查的时候需要开个set去维护一下当前的\(a[]\),当前数为x,那它一定由x / 2, x - x/2合成得到。检查其中的偶数是否在\(a[]\)中,奇数继续向下检查
  2. 如果当前数不能合成要注意恢复现场

最大公因数

思路

如果单纯枚举区间加的[L, R]再借助ST表维护区间gcd,这样复杂度是\(O(n^{2})\)。如果往一个序列加入一个新数那么\({gcd}_{新} \leq {gcd}_{旧}\)。所以当右端点固定时左端点越大越好。原序列的gcd分布肯定是一段一段形如\(2222,1111...\)。肯定是优先选择分界点作为左端点,因为前面一大堆2,多个少个都不会影响前缀gcd,和修改区间的gcd。

trick

因为一个序列gcd的种类的是\(log\)级别的,所以总的复杂度是\(O(nlogA_{i})\),固定左端点后枚举右端点,可以一边枚举一边维护区间gcd,因为gcd具有结合律.

最小生成树,DSU

思路

如果采用朴素的kruskal会爆空间,因为点太多了。可以把相同颜色的点看成一个点然后再用kruskal。当枚举到边(u,v,val)时,只需要分类讨论u和v所在的大点的连通性就行了。
这样考虑的话,每次枚举时对于小点的连通性讨论就很简单,因为大家的边权都是一样的可以直接算。

典题

思路

trick

为什么k一定是断点i的因数,因为\(i和i+1\)一定要断开,且i一定是当前段的最后一个,所以k一定整除i

数学

思路

如果直接分左右两半来算需要考虑位数,必须是n/2位才行,这样比较难做。所以想到容斥来做这样就不用考虑位数。但是需要注意的是左半边是起点应该是1,右边是0。直接开根号就可以计算出一个数内有多少平方数。

posted @ 2024-04-19 23:56  自动机  阅读(12)  评论(0)    收藏  举报