- 博客(82)
- 收藏
- 关注
原创 ABC 358
定义 dp [ i ] [ j ] 表示前 i 个字母里组成长度为 j 的字符串的方案数。对于选的 z 个第 i 个字母,实际上就是插空,插到前 i - 1 个字母组成的长度为 j - z 的字符串中,当前长度为 j,那就是 j 个空里面选 z 个。对于 dp 的每一维如何定义,就是看问题中的变量,一般第一维是以第 i 位为结尾,题目要求 1 - k 长度的方案数,那么第二维就是长度。因为转移的是方案数,所以 dp [ 0 ] [ 0 ] 初始化为 1,在枚举转移方案的时候用的是 +=。
2025-04-08 15:26:45
171
原创 牛客周赛88
dp 的无后效性是指对于前 i 个点的答案,与 i 之后的点无关。x + z 中包含了所有 x 中为 1 的二进制位,也就是说 x 加上 z 后不能破坏 x 中本身二进制位为 1 的位,因此只要找到 x 的二进制位中第一个为 0 的位,让这一位为 1,赋值给 z 即可。cnt 是实际位数,pos 是处于第几位,但 pos 是从 0 开始的,二进制表示中最低位是第 0 位。对于夹在有石子的格子中间的空格,就是受到两端石子数的限制,因此看一下距离能否满足,只可长不可短,如果长还要看奇偶性能否满足加一减一微调。
2025-04-08 13:47:10
316
原创 ABC 398
因为已经在枚举 i,那么 dx [ i ] 和 dy [ i ] 就是固定值,只要做一个移向就可以得到 dx [ j - 1 ] = dx [ i ] - r,满足这个式子的 j 就是符合条件的,那只要用一个 map 看一下当前 pair 是否存在就可以了。假设当前在时刻 i,对于第 j( j <= i ) 时刻产生的烟雾,会受到 j,j + 1,……i 时刻风的影响,也就是一个区间和,因此想到暴力写法:枚举 j。看到输出的字符串是对于每一个时间的判断,也就是肯定要枚举时间,在最外层的循环。
2025-04-01 10:30:56
194
原创 ABC 362
x1 + x2 + ……+ xn- 1 = goal - xn 要在区间 [ f [ i - 1 ], g [ i - 1 ] ] 内。xn 要在区间 [ goal - g [ i - 1 ], goal - f [ i - 1 ] ] 内。把每个区间的左端点求和,右端点求和,得到的就是总范围,看 0 是否包含在内。同时 xn 还有其本身的限制条件 [ l [ i ], r [ i ] ]对每一个 i 都只考虑左端点,那就是两个左端点里取个 max。接下来的求每一段内取哪个数,是一个倒序求解的过程。
2025-03-31 16:48:21
196
原创 ABC 360
假设枚举向左走的蚂蚁,对第 i 个向左走的蚂蚁,在向右走的蚂蚁中求出坐标大于等于 a [ i ] - 2 * t 的蚂蚁数量。把向左走和向右走的蚂蚁分别存进两个数组并排序,对于两者都要枚举的情况,思路是只枚举一个,那枚举的这个就是一个固定值,问题就转化成如何快速找到另一个变量符合条件的值。如果是要找具体的某一只蚂蚁那就得去特判是否返回的是 end ()upper 返回 end (),因为 end () 本身就是最后一个元素的下一个元素,指向的就是空节点,不会产生影响,所以也不用特判。
2025-03-31 13:25:11
353
原创 ABC 361
现在不要回到起点,可以省掉一条链上的边权和,也就是找到以起点为根,到最深的叶子节点路径上的边权和。如果题目是从一个点出发,所有点都要经过一次,并且最后要回到起点,答案就是边权和 * 2。最后的答案是 :边权和 * 2 - 树的直径。
2025-03-31 12:58:43
330
原创 牛客周赛 87
转 ll :ll num = stoll ( s )(3)int 转 string :string s = to_string ( num )string 转 int :int num = stoi ( s )2. 删三个,从 i - 3 转移过来。(1)字符不能用 string 存,只能用 char。(2)删:1. 删两个,从 i - 2 转移过来。(2)char 转 int :c - ' 0 '(1)不删:从 i - 1转移过来。
2025-03-31 12:47:39
144
原创 ABC 363
比较难想的是对于一个会被淹没的格子,它被淹没的时间取决于来淹没它的格子有多高。T 只增不减,对于当前被淹没的格子是贡献到 T 里面去的。优先队列默认是大根堆,用的是小于号,所以重载小于号。因为要从小到大,所以外面是小于号,里面是大于号,大的反而小,排在后面。这里必须要像 bfs 一样一层一层去扩展淹没,如果对一个队首的格子去 dfs 那会导致 T 偏大,所以每次只能扩一层。全排列生成:next_permutation(next_permutation(&s[1], &s[n + 1]))
2025-03-27 19:47:06
208
原创 ABC 364
将问题:找到和第 k 远的距离,转化成,二分一个距离(半径),以 bi 为圆心,有多少 a 点在圆内,如果大于等于 k 个就成立(可能会存在并列,也就是 k 和 k + 1 的距离相等)看 [ b - mid,b + mid ] 中有多少个 a 点,只需要在 a 中 lower 左端点,upper 右端点。
2025-03-27 19:02:19
228
原创 ABC 365
显然 dp [ i ] 是由 dp [ i - 1 ] 的三个状态转移过来,i 自己有一个限制(不能输),和 i - 1 有一个限制(不能一样),两种情况跳过就可以了。把剪刀石头布作为一个状态放到 dp 的第二维,则 dp [ i ] [ j ] : 前 i 轮中第 i 轮出 j 最多赢几局。
2025-03-27 18:54:53
209
原创 ABC 367
注意到取模的性质,也就是 d [ i ] % m == d [ j - 1 ] % m,因此只需要统计一下模数相等的就可以了。先断环成链,然后从 n 开始一直到 2n - 1,判断一下前缀和。注意滑动窗口两端要同时更新。
2025-03-23 11:06:23
193
原创 ABC 368
递归到叶子节点,从下往上算贡献,只要当前节点要留下,那它往上一直到根节点的路径上的节点都要留下。因为从一个父节点去搜所有子节点,该父节点只会经过一次所以不会重复计算。要等所有子节点都递归完再去判断。根节点不能随便选,必须是必须留下的 k 个里面任选一个。
2025-03-23 10:59:33
191
原创 ABC 369
n 小于 400,可以先 floyd 求出两两之间的最短路。对于必走的 k 条边,先全排列,同时要考虑双向。答案就是 k 条边内部的累积和加上 1 到全排列的第一个的起点加上全排列最后一个的终点到 n 的最短路。常规思路是 dp [ i ] 表示到前 i 个怪的最大收益值,因为要看奇偶性所以再加一维,0 表示当前这个怪打或不打共打了偶偶数怪,1 表示打。在 floyd 前要有两次初始化,一次是自己到自己置为 0,其余 INF,一次是存在边的两点赋初值。
2025-03-23 10:52:31
404
原创 ABC 370
但显然这道题要 n 才能过,而瓶颈在于 dp [ i ] 的转移过程,转移过程实质是除了不满足条件的 d [ j ],其他所有情况的方案数总和。那优化方法就是维护一个全局变量 tot 表示所有方案数总和,减掉前缀和不满足的方案数,用 map 就可以解决,用前缀和的值去映射方案数。dp [ i ] 可由 dp [ j ] ( 1 <= j < i )转移而来,但是这个转移是有条件的。我在枚举 i,那么 d [ i ] 就固定,k 也固定,所以不满足的 d [ j ] 就可以算出来且唯一。
2025-03-20 16:20:41
175
原创 牛客周赛85
任意指定一个根节点,先深搜到叶子结点,然后退上来的时候加上子树贡献,一旦到二分 mid,需要染紫的节点数就加1,最后和 k 比一下,看能不能在 k 个紫色内符合 mid。对于每一个父节点再算儿子的贡献时,一定要等所有儿子贡献全部加上再判,否则就会有子树因为提前 return 而遍历不到。遍历树不需要 vis 数组,带上 fa 不能回退就可以了。
2025-03-18 14:58:01
136
原创 ABC 372
如果正着做,对于第 x 个房子会因为前 x - 1个房子当中有高于它的而被挡住,随着 i 往后,高于它的会在界外,那就需要实时判断从 i 到 x - 1 有没有高于它的,显然不可行。出现这个问题的原因在于第 x 个房子会受前面房子的影响。但是如果倒过来做,从后往前遍历,显然第 x 个房子不会受到它后面房子的影响,无论后面的房子比它高还是低。维护每个集合前 10 大。每个集合要开到 20,拼接后 sort。维护集合信息都是以每个集合的根节点作为下标。正解是倒序维护一个单调队列。
2025-03-18 14:51:36
146
原创 ABC 373
(2)有向图是没法 dfs 的,任选一个为起点,那么走向这个起点的点所在子图就遍历不到,所以第一步先把无向图改为有向图:u —> v 边权 w 等价于 v —> u 边权 - w。每次加边同时加反向负边。(3)对每个连通块,任意一个起点,点权设为 0,然后往外推。v - u = w 就是 v = u + w。(1)不同连通块不会产生影响,每个连通块各自处理。
2025-03-18 14:42:13
273
原创 ABC 374
dfs 里的 pos 是当前在第几条线段,参数里的 a 和 b 是从上一条线段的哪个端点转移而来的横纵坐标。因为两个方向都可以,所以当前线段到下一条线段是从哪一个端点过去也会有两种情况。
2025-03-16 15:26:27
116
原创 ABC 375
看到这是一个求和公式,应该把它拆开来,变成( i - 1 )*( n - 1 )+( C1 + C2 + ……暴力的做法:设当前枚举到 i,第 i 个字符为 Cn(第 n 次出现),ans += ( i - C1 - 1 )+( i - C2 - 1 )+。三个队伍,每个队伍的力量值相等且都为总和的三分之一,这是很明显的背包特征,每个队伍的力量值就相当于是体积,且必须装满,只是背三个包。把从(x,y)到(y,n + 1 - x)分成两步,先变到(n + 1 - x,y),再变到(y,n + 1 - x)
2025-03-14 19:43:00
424
原创 ABC 376
(2)对 a 进行从小到大排序,那么 max a [ i ] 就变成了我枚举的值,变量就只剩下对可选择的 b [ i ] 选 k 个求和。此时小于 a [ i ] 的 pair 对中的 b [ i ] 都是可以选择的。(3)枚举的 a [ i ] 是肯定选的,那么捆绑的 b [ i ] 也一定选,所以就是在前 i - 1 个里面选 k - 1 个。(1) 一个环从 1 --> 1 变成 1 --> k,k --> 1。(2)求 1 到 k 的最短路和 k 到 1 的最短路,相加就是答案。
2025-03-12 20:42:11
159
原创 ABC 378
但是一旦出现具体的路径就不能 bfs 了,因为一个点只能入队一次,一个点算在了某条路径,那么其他的路径就不能再用这个点了,显然这是不可行的。根据以上结论,做法是 dfs 出所有连通块,各个连通块内的节点度数都是 3,对于每一个连通块,内部所有点向外走出一步,走到的这个节点如果度数为 2,cnt 加一,最后。假设对 u,v 添加一条边后形成一个所有节点度数都是 3 的环,那么在添加边之前,u 和 v 的度数都是 2,u 到 v 的路径上的点度数都是 3。
2025-03-12 13:41:52
280
原创 ABC 379
只要左房子满足,右房子一定满足,因此只需维护后缀,从最后一个房子一直到左房子前一个,有多少个房子能被左房子看到。右房子的作用就是加上限制条件,一定要在右房子的右边。设一个全局变量 T,表示到目前总的生长高度,再设一个 st 表示已累计出队的生长高度,T - st 就是还未拔掉的植物的高度,等价于维护了一个动态后缀和。单调队列维护可看见的房子,二分查找第一个在右房子右边的房子在队列中的下标,得到一共有多少在右房子右边且能看到的房子。求一个前缀和判断到下一个有石头的格子的前一个格子够不够填满就可以了。
2025-03-12 13:41:38
414
原创 ABC 380
并查集根节点不能任意选,因为我还要知道当前连通块的区间范围,因为染色后要和区间两段的相连节点比较颜色是否相等,看能否合并。并查集维护的是相连的同色块,还需另开两个数组,第一个数组 sz 维护每个并查集的大小,下标是根节点坐标。第二个数组 ans 维护的每种颜色有多少,下标是颜色。把字符串看成一个字符,下标从 0 开始,所以组号也是从 0 开始。当两个相连的块变成同一种颜色后,这两个块的颜色就永远相同了。看第 k 个字符是在第几组,用二进制表示,若有奇数个 1 要翻转,偶数个 1 不翻转。
2025-03-12 13:41:12
183
原创 ABC 381
c 数组存的是每一个 ' / ' 的下标,二分查找的是当前 ' / ' 是 c 数组中的第几个,c [ mid ] 才是当前 ' / ' 在整个字符串中的下标。这里是不能直接去二分在整个字符串中的下标,因为我二分的对象是存 ' / ' 的集合,也就是 c 数组,所以只能去二分是 c 数组中的第几个。对每个区间内的 ‘ / ’ 枚举会超时,但是对于所有 ‘ / ’,从前往后前面的 ‘ 1 ’ 越来越多,后面的 ‘ 2 ’ 越来越少,答案一定是先增后降,因此可以二分。r代表每一组的第一个数组,以组为单位。
2025-03-12 13:40:48
282
原创 牛客月赛111
t [ i ] 标识以第 i 个字符串结尾的最长相同连续字符串的长度,h [ i ] 标识以第 i 个字符串开头的最长相同连续字符串的长度,只要判断一下 s [ i - 1] 和 s [ i + 1] 是否相等。第二类删,枚举删哪一个,那么就是看删掉第 i 个后,第 i - 1 个和第 i + 1个能否连起来,能连连接后的长度是多少。显然不能从两个点遍历,只需要预处理一下就可以了。正确做法是二维 dp。对于当前格子只能由上面和左边的格子转移而来,只需要判断一下上面和左边的格子能否到达,以及坐标不能越界。
2025-03-09 20:39:32
152
原创 ABC 395
分层图最短路,建图方式是在前 n 个点是第一张图,n 到 2n 个点是第二张图。同时两张图的同一个点要连起来,第二张图的边要反过来。给每个巢加一个牌子,在第二个操作的时候,对两个巢只要换牌子就可以了。因此每一次询问的时候,都是输出鸽子所在巢的牌子,而不是巢的编号。任何时候换巢的索引都是牌子。
2025-03-03 18:46:33
244
原创 ABC 382
的形式,虽然只是一维,但是每一个下标对应的都是一个 vector,也相当于是二维。而一维的大小是动态扩大的,这样就避免了二维不确定要开多大的问题。既然是要查找 b,那在读入美食水平的时候存的数组下标就是美食水平,而不是现在是第几个人。对于同一个美食水平,存最靠前的人的位置。接下来就是类似前缀和。美食水平小于 b 的人都可以吃,那就是在美食水平小于 b 的里面找位置最小的,把求和改为取 min。目标是对于一个 b,要从第一个人开始,找到第一个大于 b 的人的位置。b 对应的是每个人的美食水平。
2025-03-01 16:32:13
344
原创 ABC 383
对于当前的 mid,把所有大于 mid 的路径全部删掉,看剩下来的边能否保证两个集合的点连通。对于每一条可选入的边,两个集合要合并,那么各自集合内的 a、b 要和另外一个集合内的 b、a 配对,更好的做法是把两个集合内的 a、b 都加到一个集合内去然后抵消。60 = 4 *15 = 4 * 3 * 5 = 2^2 * 3 * 5,则有3 * 2 * 2 = 12 个因数。因为 cnta 和 cntb 维护的是每个集合的信息,所以下标就是集合的更节点的下标。质因数分解,答案是每个质因数幂次加 1 的累乘。
2025-02-26 19:46:48
265
原创 ABC 394
具体方法是从路径长为 0 和 1的所有情况开始扩散,就是 bfs,每次取出一条路径的两个端点 u 和 v,检查有没有一个点 x 到 u 的字符等于 v到一个点 y 的字符,若有并且 x 到 y 还没有答案,则更新 x 到 y的答案。一定要把(i,i)也就是路径长度为 0 的先入队,不能放在循环里入,要确保这些对在队列最前面,也是最先取出。画几个烷烃会发现,设度数为 4 的点为主干,有 x 个,则总结点数是 3 * x + 2。一个点在选出来的子图上能作为主干,那么它在原图上的度数一定大于等于 4。
2025-02-26 19:24:02
439
原创 ABC 384
把 bfs 的普通队列改成优先队列,在扩展的时候优先扩数值小的。优先队列的结构体要重载一下小于号,把步数换成当前点的数值,其他的和普通 bfs 没有任何区别。具体方法是对每一个分数(1 - 31) 位运算,每次右移一位看该位是否为 1,是 1 那就说明对应的题做出来了,名字里加上对应位的字符。解决第二种问题有一个经典的做法,在一个区间后再拼接一个区间,两个区间拼合处前后就包括了一个区间的首尾,这时候用双指针也同时解决了第一个问题。优先队列里的是待吞没的,我并没有设一个普通队列来记录已吞没的,没有用处。
2025-02-24 11:20:53
129
原创 ABC 385
但是遍历所有房子复杂度太高,实际上我只需要看在横坐标为 x 的那一列上有多少房子在 y 到 y + c 的路径上,这将大大简化计数过程。set 开不了那么大,用 map < int, set < int > >,一个是同一列(x 一致),一个是同一行(y 一致)对于每一次操作,假设是往上移动,那我就看 y 到 y + c 的路径上有多少个房子。dp[ i ][ j ]:选择的 i 个建筑,间隔为 j。这点和优先队列正好相反。
2025-02-23 21:04:38
284
原创 ABC 393
(2)枚举 x,mult [ x ] = cnt [ x ] + cnt [ 2x ] + cnt [ 3x ] + ...(3)再次枚举 x,只要 mult [ x ] 大于题中给定的 k,x 就可以成为序列 A 中是 x 的整数倍的数的答案。对于每一个 0,要么移到其左侧所有 1 的左边,要么移到其右侧所有 1 的右边,选哪种看哪边 1 的个数少。对于一个公因数 x,若序列 A 中至少有 k 个数是 x 的倍数时,x 合法。目标:统计公因数 x,序列 A 中有多少个数是 x 的倍数。
2025-02-21 13:35:04
329
原创 ABC 392
概率 p :累加第 i 个骰子的 k 个面,在第 j 个骰子里出现次数,除以 ki * kj 就是这个两个骰子的概率。第一个从人到人,第二个从围兜到人,第三个从人到围兜。2. 把这个集合从 set 中删除(只需删掉这个集合的代表)3. 这个集合的代表改成 set 中第一个集合的代表(合并)优化方法是提前预处理出每一个骰子所有出现过的数字各自出现了多少次。在一个集合,就把这条边加入到 vector 中去,可以用来连接两个连通块。(2)遍历所有点,找出每个集合的代表,加入到 set 中去。
2025-02-19 18:55:48
341
原创 ABC 391
思路是对 a, b, c 分别从大到小排序,那么三组的第一个相乘肯定是最大的,接下来就像 bfs 一样,a2 * b1 * c1、a1 * b2 * c2、a1 * b1 * c2 分别加入优先队列,一层层扩下去,但要注意可能会出现重复,用 set 去重就可以了。需要明确一点的是,我需要知道第 i 层的某一个位置它现在是 0 还是 1,但不需要知道改变顶层需不需要改变它。下方三个不一样,那就要看它自己是 0 还是 1,若是 0,那下方是必定是 0 0 1,那就是看两个 0 哪个改变的花费更少。
2025-02-19 18:36:32
322
原创 ABC 390
思路是对这个最小值进行二分答案,三种维生素在读入的时候就分开来,在 check 函数里对每种维生素摄入量大于 mid 的情况下 总卡路里是多少,加到 res 中去,最后 res 和给定热量比较。用 cnt 记录箱子数量,从第一个袋子开始,可以选择合并到已有的箱子中去(用一个 for 枚举合并到哪一个箱子),也可以自成一箱。两种情况分别 dfs。题意是在不超过给定热量的前提下,三种维生素的最小值要尽可能大,问这个最小值是多少。注意一点要用 unordered_map / set,这里只要去重,不需要排序。
2025-02-17 17:18:50
242
原创 牛客周赛74
但是代码中要反过来看,要到排完序的数组中去预处理,因为判子数组时候严格单增用到的pre 数组使用的 i,即下标,而不是 a[ i ] 数值,最终必须回到原数组中去。因此只需对排过序的数组,用 map 将首元素映射尾元素,到原数组中找到符合单增的子数组看是否对应即可。将子数组的前后所有数扔到 set 中去,并在枚举子数组的同时实时更新 set,在 set 中二分查找第一个大于子数组最后一个数的数的位置 it,若 it 不是 set中第一个,看 it -- 对应的数是否小于子数组第一个数。
2025-01-10 20:10:24
321
原创 牛客周赛73
(1) 01子序列必须要 0 在前 1 在后,对于每一个 0 只对在其之后的 1 有贡献,等同于对于每一个 1,数在其前面有多少个 0。(3)正解:滑动窗口。对于窗口左端,若出去的是 0,01子序列个数减掉区间内 1 的个数;出去的是 1,01子序列数不变。(2)这道题并不时候用前缀和预处理,因为每次移动区间 01 子序列个数不是简单的减一。(4)滑动窗口用队列维护,在单独设两个变量实时记录区间内 0 和 1 的个数。
2025-01-10 20:09:59
174
原创 LQ quarter 5th
a[ 1 ] * a[ 2 ] * ... * a[ i - 1 ] * a[ i ] % 2025 就等同于 a[ 1 ] * a[ 2 ] * ... * a[ i - 1 ] % 2025 * a[ i ] % 2025,即上一次的余数 * a[ i ] 再取模。(5)题中 n 的大小有 1e5,显然开不了 dp[ 100000 ][ 2025 ] 的数组,由于只需要用到上一次的余数,由此可以使用滚动数组。(1)对于同一个数 a,a ^ a = 0,a ^ a ^ a = a。
2025-01-06 20:02:11
434
原创 牛客月赛108
(3)第二次 dfs,存下从根结点到当前结点有多少结点符合:当前结点若为红,变成蓝,平衡值会减少,会增加。减少的数量减去增加的数量大于零则当前结点变色符合条件,设为 1。相当于求了一个树上的前缀和。(2)根据是要最小还是最多,调整 if ( check ( mid ) ) 里的是 l 还是 r。(1)一个结点颜色的改变,会影响根结点到该结点链上所有的结点的平衡值。(2)第一次 dfs 遍历树,存下每个结点为根结点的子树内红蓝色总数。(1)遍历 map 的时候用 it -> second 表示键对应的值。
2025-01-04 21:59:09
300
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人