剩余不多的ACM日记
文章目录
2025/2/16
新年第一训,看着倒数日上剩余不多的时间,决定写一写日记,留下点回忆。
今天就是平凡的一天~~(开始记流水账…)~~ 上午做了上海月赛的一套丙组和半套乙组,感觉还可以。主要是题目可做。下午学习了一下独立集,基环树,以及他们的经典例题,基环树DP,其实就是环状舞会,还可以。
洛谷签到上说今天大吉,适合交友,下午下课吃完饭就收拾东西去健身房了。虽然是第一次去,我还是装成很会的样子,直接走到自由区,拿起哑铃就是热身。结果发现重量都在大了,然后…就开始学周围的大佬做动作了,哈哈哈哈(去之前心想,不能学,绝对不能学。)一个小时就回来,接着做,发现环状舞会的题目还没有补掉,就给做完了。
。。。果然去健身房是比在家练效果更好,胸疼死了,敲键盘都快没劲了。
哦,对了,今天三顿饭,餐餐吃煎饼,真是太好吃了!
晚上在打一场CF,希望成绩稍微好一点,哎,英语怎么这么让人讨厌!!!!
2025/2/24
开学第一天,下午没课,过来写题,感觉下午可以写4道题目,实际上从 2 2 2 点开始,一直到 4 : 28 4:28 4:28 才做了一道题目,慢死了。
哎,缺钱了。
暑假集训日程
7月24日-8月16日:文理楼138
8月17日-8月30日:微501
8:30~21:25 (每天锻炼身体)
8月16号之前全部是个人训练为主,8月17号之后组队训练赛。
7月25~8月1日 上海月赛 iai.sh.cn
2025/7/24 问题 E: 自动鸡
训练赛第十六场 E 自动机 模拟
出现的问题:本地可以运行,但是提交显示RE
解决方案: struct 结构体内部放置了 stirng 类型的字符串数组,更改方式是用char类型替换
char 转换成 string 类型可以直接赋值。
string 转换成 char 可以用 s.c_str()
注意: 在结构体中用stirng 类型,但是不是字符串数组的形式,可以使用
2025/7/27
乘法逆元的误区
pow在C++里是个函数,不要用来当做快速幂,因为没办法取模
pow(2,mod-2)%mod
是错的,并且赋值是错误的,输出也是错误的,但是调试查看的时候是正确的。应该是调试的时候,跑的是快速幂的程序,但是运行的时候,使用的是c++的内置函数
结论
从一排数中任意挑选两个,贡献是乘积,然后合并成一个价值为二者之和的数,如此循环往复,求最终价值。
无论怎么排序,合并,最终都一样,所以最值相同
如何求解方案数:
主观考虑,每次合并都是选择两个,并且每次合并之后都会减少一个,那么方案数就是 ∏ 2 n C i 2 \prod_{2}^{n} C_i^2 ∏2nCi2
误区:两两合并,并不等于必须让上一个合并之后的新数与下一个数合并,也可以是某两个其他数字合并。
显然的发现,当考虑到这里的时候,就很有可能陷入DP当中,有点石子合并的味道,认为很难,然后跑掉。
2025/7/29
训练赛第二十一场:问题 C: 小塔的公约数题目卡时间,卡掉了 O ( n n ) O(n\sqrt n) O(nn) 的做法,而我又想不到 l o g log log 的思路
调和级数的时间复杂度是 O ( log n ) O(\log n) O(logn) ,以后考虑优化的时候可以往调和级数上去考虑。
2025/7/30
训练赛第二十二场,问题D:村村通工程。
出错过程:第一发思路是对的,但是因为比较模糊,没有完全挖掘出性质:最小生成树中的最大边是所有生成树中最小的最大边,非常好证明:如果不是最小,那么更换成最小的最大边,最小生成树的值可以更小,与算法矛盾,顾成立。然后写代码的时候出现了一些问题
- 循环中内外层都用 i 来赋值
- 多数据输出没有换行
第二发,发现了第一个问题,但是没有看到第二个,交上去错了,就以为思路错误了,然后又去想了暴力的算法,最后超时。
但是在更改的过程中我发现了第二个问题,更改过来,但是好像没有提交,应该是写完暴力算法之后才发现然后改完交上超时。
一个非常简单的题目,大家基本上都是前一个小时做出来,自己到下午才做出来。
感觉是无法保证代码的一些低级错误不能保证,需要格外认真
2025/8/1
多重集排列计算公式
n
!
n
1
!
×
n
2
!
×
n
3
!
.
.
.
×
n
k
!
\frac{n!}{n_1!\times n_2!\times n_3!...\times n_k!}
n1!×n2!×n3!...×nk!n!
其中
n
n
n 是总个数,
n
1
!
+
n
2
!
+
n
3
!
.
.
.
+
n
k
!
n_1!+n_2!+n_3!...+n_k!
n1!+n2!+n3!...+nk! 表示每种数字的总个数
训练赛第二十四场,B:唐克的新游戏
**小错误:**我在用st表的时候,longlong f [ 1 e 6 ] [ 23 ] f[1e6][23] f[1e6][23] 会直接显示RE,但是不显示MLE,很奇怪
2025/8/3
RE小错误:
线段树开设的空间要比n大一倍,非常容易以后忘记线段树,直接抄模板,感觉数组没有越界,实则忘记线段树储存原理
2025/8/4
搜索小技巧
训练赛第二十七场:C变音量
此题用爆搜+剪枝结果是70pts,然后我限制了递归次数,在1e7之内,然后就过了。
有时候,可能最大的答案已经找到了,这个时候可以懵更多的分,这个小技巧有可能过。
但是其实,这个题看大家很快就过了,还是一发,我还是太菜了。
快速求解数组相同数的个数
直接 upper-lower ,无需考虑是否找到,备注:lower是找到第一个不小于自己大的位置,不要理解成最后一个最后一个等于自己的位置。
lower返回第一次出现该值的位置
upper返回最后一次出现该值的后一个位置,不管是否存在不
如果没有找到,二者返回值是相同的。
2025/8/6
反悔贪心: 训练赛第十六场,实在是太美题解
2025/8/6
搜索小技巧
和 8月4号的题目一样,限制一下递归次数,就过了,好像挺实用与ACM的。
小错误
训练赛第三十场,问题 A: 跳跃
并查集的 continue 写成 break
小错误
训练赛第十七场,问题问题 D: 剪切
如果发现DP值要比平时的大,大很多,这时候很有可能继承了一些不存在状态,从而连接了两个更加庞大地整体,使得最终的答案更大,但是并不合法。
这种情况往往出现于:通常用-inf 表示数组不存在,但是转移的过程中并没有注意-inf的转移,导致数据太大的时候,转移中某些值覆盖了-inf使得总贡献增大,更新了某个dp值,从而导致一条错误的转移路径。
最大值inf阈值
32位:1e9
64位:2e16
新问题
在做训练赛第十八场问题 G: 森林迷宫,想出一个问题:
给一个树,有边权,每一颗子树都可以从子树的根节点开始向子树内部延伸,要求延伸所覆盖的总边权最大。
其实是一个非常简单的DP f[u] 表示以 u 为子树延伸的最大总边权,转移有
f[u]+=max(0,f[v]+e[i].w)
如果是负数,那么就直接延伸,否则延伸到 v点,去求v的最大延伸即可。
之所以想出这个问题是因为当时让我想了很久,才发现这是一个很简单的DP。G所花费的时间要远远大于应该花费的时间,代码太长,变量太多,清零的对象还有很多,都是一些小小的细节。
2025/8/8
set小烦恼
训练赛第十八场,问题 D: 最早连续串(线段树二分+set,mid难度)
-
结构体set排序:当我们需要Set的排序和查找去重功能时,但存储的变量有必须是结构体类型的时候,就需要重载运算符,写法和优先队列相同,但是方向和优先队列相反,小于代表小于,大于代表大于
-
结构体set的调用:和平时set的迭代器使用不同,int下的迭代器可以直接加 星号获得价值,但是结构体类型的会报错,正确使用方法是用指针来做
auto it=se.begin(); int l=it->l,r=it->r; -
set的边界问题
一 自增和自减 :当在边界进行自增和自减时,等于无效,迭代器保存当前价值
二 end():当用lower_bound和upper的时候,当找不到答案时,返回最后一个值,但最后一个值并不是se.end(),所以在判断的时候,最好优先加入最小值和最大值,然后通过判断值与值之间的大小来判断是否到达边界,而不是通过迭代器是否为end,但是 begin() 表示是开始的第一个。
算法:二分线段树
用于解决多次查询,又求解区间最左或者最右的问题。
其算法的本质是:利用线段树自身的log分层,省略二分枚举的过程。
呈现形式可以看成是线段树内部的一个函数,根据具体情况具体书写。
常规用法: 用于区间排序,不过目前学到的是双log 的做法, 离线全局二分,按照线段树排01序列的原理,判断最后某个区间或者某个点的结果是否是1,从而判断当前二分的答案是否合法,以此进行二分得到答案,时间复杂度是 l o g 2 log^2 log2 的级别。
但是本题没用到,不过题解上说这算是二分线段树的板子题目
2025/8/9
训练赛被简单题卡
训练赛第三十二场:问题 B: 交换。看到交换我就头疼,又陷入先贪心,然后考虑后续交换后的结果,花费了大量时间去做,结果最后只需要一个排序就结束了。没有发现性质:只要存在一个偶数,就可以作为中枢纽带,把所有的奇数归位。
非常显然的一个性质:两个同奇偶的数字可以通过不同的一个实现交换,并且这个不同的位置还不会变。
奇怪的错误,无限RE
训练赛第三十二场:问题 I: 【树型DP】骑士I
define int long long 显示RE,但是在洛谷上就能过。
然后缩小数据范围之后,就显示 MLE ,当我更改为部分为 long long 的时候,才显示正常运行。
以后决定不再用快读了,直接cin,有些机子特别敏感,只要类型不一致,就会显示RE
基环树,图内找环技巧
我开始的想法是,DFS记录当前正在走的一条路径,当遇到曾经走过的点,那么就成环,然后fa往回跳记录即可。
但是我发现,没有办法记录两个点构成的环,因为我们在dfs的时候,通常会将连接父亲的点认为是重复边,一般会 continue
,所以这就导致我们解决不了两个点的环。
找环代码
void dfs1(int u,int pre)
{
fa[u]=pre; vis2[u]=++num;
for (int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if (v==pre) continue;
if (vis2[v])
{
if (vis2[v]<vis2[u]) continue;
int x=v;
while (x!=u)
{
st[++top]=x;
vis[x]=1;//标记在环上
x=fa[x];
}
st[++top]=u;
vis[u]=1;
}
dfs1(v,u);
}
原理,vis2中存储的是每个点的dfs序,我们知道对于一个环,我们会在dfs时遇到两次,第一次是子节v点访问到曾祖父节点,形成环,第二次是回溯时,回溯到曾祖父节点,然后继续枚举该点连接的其他点时,枚举到子节点。对于第一次相遇,其实和我之前的想法是一样,我们普遍认为,最好想的找环就是遇到曾经遇到的,但是解决不了上述的问题。
而第二次相遇就可以很好的避免上述问题,哪怕是只有两个点的自环,v 节点也会被同样访问两次,因为建边的时候会建立两组相同的边。
基环树知识
n 个点,n条边,可能是基环树,也可能是基环森林,这个需要特别注意。
最大基环森林(基环树)的生成
训练赛第十八场:问题 I: 未来城市
求解最大基环森林,它不等于先完成最大生成森林后再加边,而是在并查集连接的基础上,一直保持边数小于等于点数。原因,最大生成森林中的联通块可以在去掉一条边后,形成两个基环树,存在两个基环树的新边之和大于当前去掉的边和完成当前联通块成为基环树的新边。
2025/8/10
不读题被卡
训练赛第三十三场:问题 B: 胖虎的菜品。
没有读到输入,坐标是实数,导致写完的爆搜感觉是对的,结果最终但不是TLE而是WA,就一直怀疑自己的爆搜出问题,直到最后看到实数,读题读题,没想到坐标还有实数的!!!!
cin加速切勿用Read
用了cin加速,千万不要用 read,尤其是T的位置,很容易忽略,最终跑出来的答案是错误,不会显示TLE,会显示WA
2025/8/11
[1,n] 中是 x x x 的倍数的个数一共有 n / x n/x n/x 个,因为,直到 n / x × x n/x\times x n/x×x 之前,所有数字都小于 n n n ,并且都含有 x x x 这个因数,因此结论成立。
如何求解含有的某个质因数的个数?
操作方式:不断除以该质因数,每除以一次,算一次个数
while (x)
{
sum+=x/5;
x/=5;
}
以五为例,求解5的质因数总个数,等价于求解5,25,125。。。的倍数总个数。写成代码,就可以理解成,先计算所有包含第一个五的,再计算所有包含第二个五的。。。。其中25的第一个五被算入第一波除法中,之后的第二个五算入第二波除法当中。
π取值越精确,分越高
训练赛第二十八场,B,题目没有要求π的取值,只是要求最后答案保留整数,结果最后精度越精确,分值越高。
π的取值:3.14159265
经典问题
给一个01串,每次可以选择区间长度为 k 进行翻转,求全部变成0的最小次数,和此时的区间长度 k
一开始我还以为和以前一样存在性质,但是实际上就是从头开始一步一步算,因为每一步是否进行翻转,取决于前面的状态,并且对于一组合法的翻转组合,其翻转顺序不影响翻转结果。这就保证了可以从左开始。至于为什么是最小,因为某一个点如果不合法,当前必须翻转。
方法,变形尺取法,其实就是维护一个区间长度为k内翻转的次数,以此来判断对于下个位置 i ,前面 k 个位置有几个发生翻转,从而得到当前现存的状态,然后再根据状态决定是否翻转。
2025/8/12
队列的运用
- 两个对顶队列,可以求出当前数列中的第i小值
- 合并石子变形题目,每次两个最小的木板,而不是求单块木板的贡献。
DP新维护
训练赛第二十八场:问题 C: 取最大值

对于维护一个下列式子的最大值,没有想到竟然可以用DP依次维护
我们用四个DP方程表示 fa,fb,fc,fd,分别表示:从1-i中,满足式子的最大值是多少。
拿 d 举例,为什么要先维护A后维护d ,对于fd我们有如下转移:
f
d
[
i
]
=
m
a
x
(
f
d
[
i
−
1
]
,
f
a
[
i
−
1
]
+
p
r
e
a
[
i
]
)
fd[i]=max(fd[i-1],fa[i-1]+prea[i])
fd[i]=max(fd[i−1],fa[i−1]+prea[i])
转移的意思就是,当前这个点我选不选的问题,如果不选,那就是
f
d
[
i
−
1
]
fd[i-1]
fd[i−1],如果选,那问题就退化成我只需要找到1-i-1中 fa最大值就可以了。初始值的确定有点难度,因为取值不同 a最小是0 d最小是1 c最小是2 b 最小是3
这本来是一个需要四个循环才能完成的事情,没想到通过这样的DP巧妙的实现。
四个动点的问题,依次转化成了确定一个求其他三个max,确定两个求其他两个的max,问题逐渐简单。
有点想数学里面的二维函数, f ( x , y ) f(x,y) f(x,y) ,我记得曾经做到过,先把y当参数,求解 f ( x ) f(x) f(x) 最大值,之后再在y中求最值。
搜了搜AI,这玩意交分部优化策略,其成立的条件是所有函数具备可加性,各个参数之间不存在关系。刚好符合上述表达式。第二种符合要求的条件是单调性,不过没看懂,对现在没啥用,先不看

334

被折叠的 条评论
为什么被折叠?



