- 博客(112)
- 收藏
- 关注
原创 ABC 334
当没有思路的时候,就去想把什么作为枚举量,显然这里枚举哪一只袜子丢弃不配对。容易证明丢弃的袜子一定是奇数位。首先明确,最后是要查询 n 匹鹿能拉多少雪橇,那么用来查询的答案数组的下标就一定是鹿的匹数,这样就能实现 O(1) 的查询。在枚举红色的点的时候,将其四周的绿色的点所在集合的代表存入 set 即可去重,set.size()就是红点直接相邻的绿色集合数。对于每一红色的点,如果将它染成绿色会导致与它相邻的绿色连通块合并,那么连通块数量是减少的。看到判连通块数量,而且后续存在连通块合并的操作,用并查集。
2025-06-13 14:48:24
245
原创 ABC 336
(1)第 i 个数如果比 f [ i - 1 ] 大,如果大超过 1,例如 2,7,那这个 7 是没用的,因为后一个最多比前一个数大 1,所以化简为 2,3。(2)第 i 个数如果比 f [ i - 1 ] 小,那 f [ i ] 可以直接取 a [ i ],而不是 1。很容易想到以:第 i 个数作为峰值,为枚举量,因为第 i 个数两边都要考虑,所以维护一个前缀和一个后缀。设 a 为原数组,f 为赋值后的数组。
2025-06-13 09:08:10
222
原创 ABC 337
对于每个非 ' x ' 的点,都作为起点分别向右或向下枚举,如果 k 个之内出现 ' x ' 就退出,否则对出现的 ' . ' 的个数取 min。注意到这里固定了区间长度 k,典型的滑动窗口解法。统计窗口内 ' . ' 和 ' x ' 的个数即可。
2025-06-11 14:42:37
240
原创 ABC 409
预处理部分:算出每个点在圆周上的位置,也就是距离的前缀和模 L。注意的是,圆上的位置标号最好是从 0 开始,一直到 L - 1,这样能够直接取模。如果是从 1 到 L,当前缀和就等于 L 时,模 L 等于 0,这时候需要加一个判断:如果等于 0,则赋值为 L。先观察性质:等边三角形,三条边对应弧长相等,而且弧长一定是 L / 3,只需要枚举起点,另外两个的可以用起点加弧长来求得。
2025-06-10 14:19:36
207
原创 ABC 341
通过容斥原理,对于 mid,mid / n + mid / m 包含了小于 mid 的数中,同时是 n 和 m 的数,因为既是 n 的倍数,又是 m 的倍数,所以最后要减掉两遍。减两遍的乘二要放在后面,只能写成 mid / lcm(n, m) * 2,而不能写成 2 * mid / lcm(n, m)观察到数据范围过大,1e10 的数据范围只能用 log 的做法,直接考虑二分。因为最后是要输出的具体的数,所以二分的对象就是数值。看它是题意中的第几小。
2025-06-05 16:06:51
170
原创 ABC 342
对于每一个输入的 a [ i ],把它所有的完全平方因子全部除掉,最后得到的数一定不是一个完全平方数,那如果要构成完全平方数,就需要统计有多少和它一样的数。在统计合法对时,为了避免重复,方法是对于第 i 个数,只在前 i - 1 个数里面算上第 i 个数的贡献,而不去看它之后的,这样所有合法对都只会算一次。看到是对字母进行操作的,一定先想能不能通过遍历 26 个字母来实现。对于把字母 c1 改成字母 c2,只要遍历 26 个字母,对第 i 个字母如果它映射到字母 c1,那就改成映射到 c2。
2025-06-05 11:33:39
281
原创 ABC 346
(1)i 为偶:最终字符串一定是 10101001010。i 之前是 1010,i + 1 之后实际上应该是 0101,这样才能在 i + 2 位是 1。i 之前是 0101,i + 1 之后实际上应该是 1010,这样才能在 i + 2 位是 0。本题中只有一对 i,i + 1 对应的字符相等,其它的都相邻字符都不能相等。i 之前是 0101,i + 1 之后是 1010。i 之前是 1010,i + 1 之后是 0101。(1)这一对相邻字符相等,是都是 '0',还是都是 '1'。
2025-06-03 13:29:27
234
原创 ABC 349
要增大的速度快,就是让前部分 2^n 尽可能大。只需要提前打个表把 2 的 60 幂次之内全部算出来,然后倒序枚举,如果不超过,并且是它的倍数,才能够加上。注意并不是所有的偶数都可以写成 2^n 的形式,也不是所有偶数都是 2^n 的倍数。只能通过打表而不能 / 2。
2025-06-03 11:18:58
168
原创 ABC 408
答案就是区间 [ 1, l - 1 ] 内的 1 要改成 0,区间 [ l, r ] 内的 0 要改成 1,区间 [ r + 1, n ] 内的 1 要改成 0。对于枚举的每一个左端点,答案是以上三段累加。接下来就变成了经典的要枚举两个变量的问题,既要枚举左端点,又要枚举右端点。方法是只去枚举左端点,然后快速查询右边哪个点作为右端点答案最小。将上述表达式分类成 l 和 r 相关,对于 r 相关求一个后缀 min 即可快速查询。最终哪些 1 要留下来太复杂,直接枚举最终 1 的连续段在哪。
2025-06-03 11:11:03
180
原创 ABC 377
i 向左移的时候,一旦有新的区间的左端点在 i 右边(包括 i),就去看所有右端点的最小值是否能更新。如果倒序做,i 从 n 开始,最开始没有区间符合条件,没有左端点在 i 右边的区间,随着 i 不断减小,会有越来越多的区间要被纳入 i 的右侧,有可能会被全覆盖。如果正序做,i 从 1 开始,那在最开始的时候就会有很多区间的左端点在 i 的右边。倒序是从少到多,递增的过程,不断有变量开始符合条件。首先思考到,对于枚举的 i(作为答案区间的左端点),需要找到所有左端点在 i 右边的区间中,右端点的最小值。
2025-05-27 16:13:00
271
原创 ABC 350
记忆化搜索是带返回值的,输出的时候如果要带小数点,用 printf ( " %.15f ", ans )记忆化搜索真的是个好东西,是正向思维,比 dp 简单很多,以后要多用,能搜就搜。期望实际上就是把每一种情况的答案答案都算出来,然后取个平均值 ,并不困难。从大规模向小规模,用记忆化搜索,只需要分好类,有哪几种搜法。要把所有的 f [ i ] 都移到等号左边。
2025-05-27 13:35:51
247
原创 ABC 352
因为窗口长度是固定的,所以队列中的下标差值最大值就是固定的,判是否有元素过期(已经从窗口左端出去了),是看 q [ t ] 与 q [ h ] 的差是否超出窗口大小,而不是看队列里的数量是否超出窗口大小。因此只需要遍历 1 到 n,固定窗口大小为 k,下标就是数,而下标对应的值是这个数字在 p 数组中出现的下标,通过单调队列维护区间内的最大值和最小值。先按照边权值从小到大排序,从小的开始,每个集合选第一个作为代表,遍历剩下的,如果不在一个集合就合并,一旦加满 n - 1 条边就退出。
2025-05-27 13:22:07
219
原创 ABC 353
每一个 ai 作为 Ai 共 n - i 次,作为 Aj 共 i - 1 次,按每个数算贡献。接下来找出哪些对之和超出了 1e8,统计这样的对的个数,再拿之前的答案减掉 个数 * 1e8。所有都先不取模,每个数出现 n - 1次,先算出不取模的答案。g 数组一定要每一个都取模。
2025-05-21 15:23:54
332
原创 ABC 354
将成本值从小到大排序,然后枚举,一旦出现当前的力量值不是最高,那当前这个就会删掉。因为在它之前有成本更低(排序已经决定),力量更高的卡牌。因此只需实时维护一个之前出现过的力量最大值即可。也可以将逻辑反过来,将力量值从小到大排序,去看后面有没有成本值更低的。这样就需要提前维护一个后缀成本最低值数组。碰到这种有两个变量的,一般是对一个变量排序,然后去判断另一个变量。
2025-05-21 15:02:20
144
原创 ABC 355
n 个区间选两个共 n * ( n - 1 ) / 2,减掉两两不相交的数量,就是答案。但是反过来思考,两个区间不相交只会有两种情况:Ri < Lj 和 Rj < Li。对左右端点升序排序后,枚举右端点,找到大于它的第一个左端点,后面所有的都符合。首先思考两个区间相交会有哪些情况:有两种左右端点包含,一种大区间包含小区间。
2025-05-20 19:21:59
261
原创 ABC 356
拿到一道题,首先先去看数据范围。n 只有 15,也就是所有的状态 2^n 可以接受,因此直接去枚举所有钥匙状态。通过 dfs 搜出所有状态,再去遍历 m 种已知条件,一旦出现不符合的直接返回。看了数据范围就会发现这道题不需要什么算法以及结论,直接暴力就可以了。一个小技巧是用一个 01 字符串来表示每把钥匙的真假。
2025-05-20 17:24:22
213
原创 ABC357
因为是拓扑序列,所以在每一次 ans 加上 f [ u ] 的时候,u 的所有子节点 v 都一定已经加到 f [ u ] 上了,此时遍历到 u,就可以算上 u 的完整的贡献,不会说后面 u 还会再接子节点。当 a^b 中 b 幂次过大时,在模 mod 意义下,a^b % mod 等价于 a^( b % (mod - 1) ) % mod。不能把所有 f 赋初值 1,这样在算链到环和环到环会有重复,环上的某个节点的子树大小不包含其自身,因为会在环到环的时候算。因此把 10^L 作为公比。
2025-05-20 14:24:32
241
原创 ABC406
因此枚举每一个连续的下升段。关键点就在发现这个性质,找到一个枚举量是很多题目的切入点。(1)取出 a.size(),记为 cnt1,作为第一段升序元素数量,同时清空 a。波浪的特征:一段连续的上升段 + 一段连续的下升段 + 一段连续的上升段。
2025-05-20 09:49:51
239
原创 牛客周赛 93
此时显然会有一个问题,如果有多个相等的最大值,如何操作。字典序是前缀越大越好,所以如果出现相等的最大值,那就让下标大的优先加到 a [ 1 ]。对于每一个数字,假设有 n 个,则共有 2^n 种选择,但是要减掉全不选的情况,所以是 2^n - 1。最大化字典序,即最大化 a [ 1 ]。优先队列,k 次内把最大的加到 a [ 1 ] 上去。最后还要加上只选一种数字的情况。从 1 开始,若从 0 开始会重复。序列必须是连续段,一旦跳过一个数就不成立。
2025-05-20 09:42:45
150
原创 牛客周赛82
根据模拟样例发现一个性质:每次在填数的时候,不关心填什么数,而重要的是此时有多少数可以填,至于填哪一个,只要这个数解锁了,什么时候填都是一样。= a [ i - 1 ]:说明出现了更小的数,此位置只能是当前这个数。但此操作会解锁更多可以填的数,解锁的数是当前这个数和上一个数之间的数,要更新可用数的数量。a [ i ] == a [ i - 1 ]:更新 ans,同时可用数的数量减一,不管是填的哪一个数,都用掉了一个。a [ i ] > a [ i - 1 ]:直接 return 0。
2025-05-13 13:46:09
279
原创 洛谷P1414 gcd调和级数
例如 tot [ 5 ] = 3,假设序列 a 中包含 10,20,35。5 可以成为选 1、2、3 个数的最大公因数,但不能是 4 及以上个数的最大公因数。找最大公因数的方法不是直接去找,而是从 1 开始枚举每个数,看它能成为序列中多少个数的公因数。tot [ i ] :在序列中有多少个数包含 i 这个公因数。
2025-05-13 13:37:21
177
原创 双指针思路
左端点在 for 循环里自己会增加,不需要手动去移动左端点。(2) 如果当前符合条件,不断移动右端点,直到无法移动。不重合:看左端点移动的时候是否需要维护信息。例题:ABC 381 / D。(4) 判断左右端点是否重合。重合:强制移动右端点。
2025-05-07 16:40:19
182
原创 Trie树
根据异或性质:a ^ b ^ a = b 可知,找两个节点 a 和 b 的异或值,就是用根节点到 a 路径的异或和以及根节点到 b 路径的异或和,去异或。如果 a 和 b 在根节点的同一个分支,那重复的都抵消了。每输入一个单词,先去查询,把它所有经过的节点的累计次数全部加到 ans 中去。然后再插入这个单词。只需要把 cnt 的定义变一下,改成有多少个单词的前缀经过这个节点。在一遍遍历树的时候,把根节点到所有节点的异或值全部都插入树里。对于 n 个数字,在所有两辆异或的结果中,求出最大值。
2025-05-07 16:32:54
306
原创 ABC 403
(2)y 集合现存的所有字符串,所有的前缀(每一个字符串的所有前缀)。这样在 x 集合中插入一个字符串,只需要去 y 里面查找是否有拥有该前缀的字符串,如果有就全部删掉。与此题类似的是不相邻取数,区别在于不相邻取数针对的是下标,限定条件在下标上,不能够取相邻的下标。维护第一个只需要一个 set,第二个用 map <int, set<int> >,键可以索引到哈希值,后面跟了一个 set 就是这个哈希值集合内的字符串编号。关键的一个处理是,把题目中要求的最少删多少转化成最多选多少,就转化成了不相邻取数问题。
2025-05-07 16:32:41
393
原创 牛客周赛91
必须明确的是,并查集一定是针对下标而不是值,因为最后遍历 fa 数组是看 fa [ i ] 是否等于 i,而这个 i 就是下标,所以这里需要改动一下。对于 ai,只看前不看后就可以避免重复,如果 ai - 1 存在,那 ai 就可以和 ai - 1 连接变成一个连续段。在遍历 map 的时候,auto 的 x 是一个 pair 类型,first 是 map 中的键,second 是值。但这里要注意的是,如果 ai - 1 和 ai + 1 都不存在,还要加上 ai 个数 - 1。(2) 找连续段段数。
2025-05-07 16:32:15
65
原创 牛客月赛115
对于第 i 个字符串(下标从 1 开始) ,第 1 位、第 x + 1 位、第 2x + 1 位,都变成了第一位,所有字符串都是如此。首先预处理出值域内所有值分别有多少个,在遍历到 i 时,把 ai 所在桶对应值减 1,而要找到 i 之后大于 ai 的有多少个,只要去遍历大于 ai 的桶进行求和。因为是要找最小的公倍字符串,所以要求循环节最长,因此是直接求出所有字符串长度的最大公因数,作为循环节长度。然后开始修改,对于第 1 位,统计所有出现字符的个数,全部修改成出现次数最多的那个字符,剩下的位以此类推。
2025-05-07 16:31:57
72
原创 牛客周赛90
就下来就是判断 25 个数是否超过 mid,一旦超过就重新开一段。同时得记录当前单个字符串的 2 和 25 的个数,这样如果把当前的与之前的合并超过的话,那整体的可以立即重置为当前的。对于每一个 mid,如果按找这个 mid 来分最少分成 x 段,只要 x 不超过 k,那么这个 mid 就成立。如果叶子数是偶数,两两染。如果是奇数,剩下的一个叶子去和整棵树权值最小的点一起染。直接去猜结论:既然所有的节点都要染,那叶子也要染,所以只要直接染所有叶子。按照二分答案的思路去思考,会发现切成 k 段是切入点。
2025-05-07 16:31:35
64
原创 牛客月赛114
假设现在要摸 k 张牌,枚举从头摸 i 张,那从尾就是摸 k - i 张,当然也可以小于 k - i 张,并不是一定要摸满 k 张。此时只需要维护一个后缀最大值就能快速查询。先二分出最多进行局数,也就是最多摸几张牌。对于枚举的摸牌次数,头和尾各摸几张不确定,对于这种有两个变量要枚举的情况,方法是只去枚举一个,另一个用枚举的变量表示出来。td 数组存的是前 i 轮一共弃置掉的手牌数,dc 数组是前 i 轮结束后还剩多少手牌。
2025-04-15 13:45:24
169
原创 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
239
原创 牛客周赛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
372
原创 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
233
原创 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
229
原创 ABC 360
假设枚举向左走的蚂蚁,对第 i 个向左走的蚂蚁,在向右走的蚂蚁中求出坐标大于等于 a [ i ] - 2 * t 的蚂蚁数量。把向左走和向右走的蚂蚁分别存进两个数组并排序,对于两者都要枚举的情况,思路是只枚举一个,那枚举的这个就是一个固定值,问题就转化成如何快速找到另一个变量符合条件的值。如果是要找具体的某一只蚂蚁那就得去特判是否返回的是 end ()upper 返回 end (),因为 end () 本身就是最后一个元素的下一个元素,指向的就是空节点,不会产生影响,所以也不用特判。
2025-03-31 13:25:11
395
原创 ABC 361
现在不要回到起点,可以省掉一条链上的边权和,也就是找到以起点为根,到最深的叶子节点路径上的边权和。如果题目是从一个点出发,所有点都要经过一次,并且最后要回到起点,答案就是边权和 * 2。最后的答案是 :边权和 * 2 - 树的直径。
2025-03-31 12:58:43
430
原创 牛客周赛 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
181
原创 ABC 363
比较难想的是对于一个会被淹没的格子,它被淹没的时间取决于来淹没它的格子有多高。T 只增不减,对于当前被淹没的格子是贡献到 T 里面去的。优先队列默认是大根堆,用的是小于号,所以重载小于号。因为要从小到大,所以外面是小于号,里面是大于号,大的反而小,排在后面。这里必须要像 bfs 一样一层一层去扩展淹没,如果对一个队首的格子去 dfs 那会导致 T 偏大,所以每次只能扩一层。全排列生成:next_permutation(next_permutation(&s[1], &s[n + 1]))
2025-03-27 19:47:06
263
原创 ABC 364
将问题:找到和第 k 远的距离,转化成,二分一个距离(半径),以 bi 为圆心,有多少 a 点在圆内,如果大于等于 k 个就成立(可能会存在并列,也就是 k 和 k + 1 的距离相等)看 [ b - mid,b + mid ] 中有多少个 a 点,只需要在 a 中 lower 左端点,upper 右端点。
2025-03-27 19:02:19
272
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅