考场tips/记trick
又:奇怪挂分点 或 做题手法
1.调和级数O(n/1+n/2+n/3+...+n/n)=O(nlogn)
2.树上问题中边和点的相互转化,染色,多次查询树剖+线段树。
3.3221225477 (0xC0000005): 访问越界,一般是读或写了野指针指向的内存。
3221225725 (0xC00000FD): 堆栈溢出,一般是无穷递归造成的。
3221225620 (0xC0000094): 除0错误,一般发生在整型数据除了0的时候。
4.恰好与至少的相互转化,容斥原理或二项式反演。
5.用 vector
6.根号分治的想法。主要在于观察题目条件,得到暴力美学的做法,经常和操作次数、操作方式和每次操作值域有关。比如 排列最小生成树 (pmst) ,想到 \(|i-j|*|p_i-p_j|\) 超过n是不优的,又有排列的条件,从而得到操作不超过根号n条边。又比如 CF1207F Remainder Problem ,经典应用吧,对于小于根n的位置维护 \(b_{i,j}\) 表示当前所有下标模i的结果为j的数的和,对于所有大于根n的位置,直接在询问答案时暴力枚举下标,均摊复杂度 \(O(q\sqrt{n})\) .
7.枚举子集的子集的时间复杂度是 \(O(3^n)\) ,可以把式子展开使用二项式定理证明。推广:枚举k次子集的时间复杂度是 \(O((k+1)^n)\) .
8.永远写不对的dijkstra……(和现在已经不会写的SPFA)要关注两个算法的适用范围,还有vis,以及加入队列的-dis。仔细检查最短路!还有如果在外面更新了dis,那么一定要把当前点入队,才能去更新别的!!!这是动态转移和floyd的区别之处。
9.读题读题读题,审题审题审题!!!题目条件一定要看清楚再写,可以把条件或者自己想到的东西先写到本子上,写代码的时候一定要关注。题面看不懂就多看几遍,一个词一个词的看,尤其是t1t2不能因为看不懂而不写。多读题多读题,题读百遍其义自见。
10.容斥原理,二项式反演,至少/至多和恰好的转化。还有写的时候注意组合数和快速幂的正确性。
11.一定要判无解!!!!
12.最短路问题,如果要考虑一些性质,可以建立最短路树,如果最短路不唯一,则是最短路图。
13.双指针,把O(n^2)等转换为O(n). 数据结构枚举左端点查询右端点,或者扫描线维护二维结构。
14.平面图和对偶图的相互转化。简单来说(可能不严谨),平面图是边将图划分成为若干个不相交的面(比如网格),对偶图是把平面图的每个面化为一个点,如果两面相邻,则对该两点连一条边。经典手法是求平面图的划分数等价于对偶图的路径数。
15.快排重载运算符的时候不能加等号,比如写<=就容易re,要写成<。本质上是由于快排的内部程序结构中while引起的。
16.关于质因数分解的常用结论:一个数所含质因数最多log级别。(质数>=2)
以及卡常小技巧,特判2,然后从3开始,i+=2,只考虑剩下的奇数
17.vector加sort比set快很多!
18.像存权值这种可能相等的东西,一定要用multiset!并且每次删除元素一定要按照地址删,如果按照权值删,会把所有当前权值的都删掉。
19.图的边权形如|i-j|的直接贪心走/连相邻标号节点!
20.一旦题目和什么gcd或者质因数分解有关,一定要注意其情况数上界很有可能是log级别的。甚至于有方法是枚举答案去check。
21.分解质因数和寻找因子的最坏复杂度都是根号。
22.优先队列重载运算符,注意要重载小于号。
struct node{
int x;
int y;
bool operator<(const node &a) const
{
return x>a.x;//从小到大排序
}
};
23.算空间!!一旦开大直接->0pts!!!
24.对于只有加操作并需要取模的dp,当时限很紧时,不要开longlong+手写取模+快读!!
25.对于只有三种不同颜色去排列的方案,dp时一定要记得设上一次出现的位置为i,j,k,再根据下一个放什么去转移!(同类型的题目见一次不会一次,真服了)
26.取模取模取模!!即使是部分分也要记得取模!
27.对于计算本质不同的子序列,可以 \(O(n^2)\) \(dp\),设 \(dp_i\) 表示以i结尾的本质不同的子序列个数。转移时钦定只对它靠一侧的匹配转移。
可以优化到 \(O(n)\) ,即设 \(dp_i\) 表示以 \(\leq i\) 结尾的本质不同的子序列个数。但是两种写法一定要分清楚不要混了。
28.一定要加c++14编译环境,以及开大栈空间!-std=c++14 -Wl,--stack=100000000
29.关于异或运算的处理手法,问到大于小于的问题一定要想到一定是:前k位相等,第k+1位不等。而且很多异或的条件可能只是和相邻两项有关,这个细节也是很好用的。可以考虑01trie(当然能简单维护就不要复杂化)。
30.对于每个区间求方案数或者贡献的问题,套路性地把某个点i固定下来,然后对它求贡献,可以用数据结构维护。
31.大量访问时建议使用vector而非链式前向星,因为vector的下标连续,在常数上更优!否则会被卡常!!!
32.二维数组一定要把大的那一维放前面,小的放后面,会快很多。
33.对于求一个字符串满足某些区间内0和1数量相等,不防记0为-1,记1为1,求前缀和,则该区间前后sum相等。或者直接0当0,1当1,然后列出来题目限制的若干条件。
这东西叫 差分约束系统 。对于每个约束条件 \(x_i-x_j \leq c_k\) ,都可以变形成 \(x_i \leq x_j + c_k\) ,这和单源最短路中三角形不等式非常像。那么我们把每个变量 \(x_i\) 当作图中的一个节点,对每个约束条件,从节点j向节点i连一条长度为 \(c_k\) 的有向边。(带绝对值就是双向边,注意对于本题还要建立原本的 \(|d_i-d_{i-1}|=1\))
注意到,对于一组解 \({a_1,a_2,...,a_n}\) ,对于任意常数d, \({a_1+d,a_2+d,...,a_n+d}\) 也成立,因为做差后d刚好抵消。
过程:设dis[0]=0并向每个点连一条权重为0的边,跑单源最短路。若图中存在负环,则给定的差分约束系统无解。
34.卡时的时候一定要写成(double)clock()/CLOCKS_PER_SEC转化成秒,因为本地和评测机的这个周期不一定一样!虽然也不是很理解这个为什么要加double,但是不加绝对会挂!
35.一定要看是否要开long long或者__int128 !!! c++14允许使用__int128了!注意手写输入输出。但是如果范围超过__int128就要写高精了。
36.场上一定要先看四道题,避免死磕一道把时间浪费完了。难度可能不是顺序。遇到简单的部分分也可以先写。(我就只有这一场不是开始先看全部的题,结果就今天不是顺序。。。)
37.看时限,看空限,即使是部分分也要看!有时候题目会给出很奇怪的时空限制!比如4000ms,16MiB之类。。甚至于会有非常小范围的n,k这种,直接暴力会比带值域log快!!注意卡时
38.能直接推导就不要过分转换,否则边界之类的分讨很麻烦。(一旦转换就要注意边界条件)
39.草稿上不要太凌乱,适时想想有没有脑中一闪而过又忘记的东西,注意把想到的都记下来。
40.checker 的使用方法。把所有东西放到同一个文件夹,在“此电脑”中打开,并在上方地址栏输入cmd,然后按照题目格式放入checker和in/out文件。
另外,构造题先研究样例输出,先试图瞪出规律。否则构造还是需要严谨。
41.启发式合并的想法,有时候有题目需要维护集合,并且合并顺序可以自定,那么把小的向大的合并,是O(nlogn)的。会很方便。理解上就是,每产生一次合并,集合大小至少翻倍,那么对于n个集合,每个集合最多参与log(n)次合并。
42.bitset奇妙函数:
for(int j=tmp._Find_first();j<=m;j=tmp._Find_next(j))
//可以遍历其中所有的1的位置
b.count();
//计算其中1的个数
43.能少取模就少取模,能不开longlong就不开longlong,小心t飞。但是别忘了可能越界的地方要*1ll,相减要+mod !!!
44.一般遇到瓶颈,想不到算法或者怎么维护的时候,大概率是题目性质没发现完全。多读题多手算样例,挖掘题目本身。反正如果真是什么手法算法没想到,那肯定是我不会的东西啊,场上也不可能写出来
45.对于图上路径处理/找环的一个重要想法就是dfs树上,考虑树边、非树边(横插边、返祖边),打标记去判断。
46.经典手法,把排序或者比大小改成维护0,1,即比当前小的为0,比当前大的为1。(有时可以使用线段树维护01排序)
47.dfs中定义局部变量和参数都会占用内存!!如果dfs层数特别多,内存很可能爆炸!所以能不写dfs最好别写。(我终于明白了回文自动机的parent tree为什么要写成基数排序的形式去遍历。)
48.别相信时限,尽可能用空间换时间,空限在不超的情况下开满!(可以在任务管理器处查看程序空间使用情况)
49.同余最短路,在mod某个数的意义下,建边,跑最短路,可以 \(O(n^2logn)\) 求出达到加和与某数同余的最少次数或者价值。常用于解决,给定一个上界和一些数,求通过特定方式的加和能达到的不同数的个数。
50.一个字符串的所有border(最长公共前后缀)排序后,至少存在一种将序列划分为若干子序列的方案,使得所有子序列均为等差数列,且子序列个数为 \(O(log|s|)\)
引理1:s[0..k-1]为s的border,等价于|s]-k为s的周期(由定义得)
引理2:考虑两个|s|的border A,B (|A|<|B|),则A也为B的border(由定义得)
则对于长度 \(\geq \frac{|s|}{2}\) 的border,它们的gcd为p,则p也为|s|的周期。构成等差数列。
考虑剩下的border,它们都是其中最长的border的子串。考虑递归处理,至多log|s|层。
51.并查集(没有路径压缩时)一定要写按深度合并!按秩合并中按siz合并复杂度要远大于按rank合并!会被卡!!
52.太典了!又是无向图建了个单向边?!
53.没逝的没逝的,只不过线段树合并又没写返回值罢了。
54.结论:求满足区间内每个数都小于端点的区间数,除了端点相邻的区间,不会超过n个。求的时候可以考虑求出每个点左右第一个大于它的位置l,r,则贡献区间就是[l,r]。再加上相邻区间即可。
55.数颜色问题的套路:
法一,记录颜色上一次出现的位置pre,即查询有多少 \(i\in[l,r]\),满足 \(pre_i\in[0,l)\)。
法二,用扫描线和区间加法。从i转移到i+1时,区间 \((pre_{i+1},i+1]\) 这一段颜色数量会+1,我们可以计算当 \(i\) 为区间右端点的贡献,即维护后缀和,在 \(i+1\) 处+1,在 \(pre_{i+1}\) 处-1。
56.queue的swap函数是O(1)的!因为只需要交换指针。
57.mt19937 rnd(time(nullptr)); 生成32位无符号整型伪随机数,更随机,更快。rnd里面实际上是seed,可以取时间。
58.manacher的数组一定要开二倍!!注意细节和判断边界!!
59.按理来说点分治跑两遍getrt更严谨,但可能会被卡常!其实只跑一遍getrt的复杂度也是对的。。。(好像有证明
60.独特的推式子方法:\(\sum_{j=1}^{i-1}[gcd(i,j)=1]*j=\frac{\varphi (i)*i}{2}\),可以枚举并感性理解,其中和i互质的数都能两两配对,使之和为i。
61.注意到,位运算的优先级低于算术运算符和比较运算符!!又是被位运算优先级弄死的一天。。。且注意到,如果可能炸int,就要写 1ll<<x !
62.任意一个正整数可以被表示成形如\(\binom{n}{2}\)的三个组合数之和。奇妙引理,找不到证明。
63.神秘结论之,随机生成一棵有根树的期望树高是根n。按照某种特定方式,如,规定树边从小编号连到大编号,生成的随机树,期望树高是logn。
64.只有期望有线性性,所以不要在期望的题里很作的去用总方案搞,步骤间还是要用期望去做。
65.把数组定义在函数里会死,不仅是初始化的问题,它调用完函数之后那个内存不太会释放的样子,而且评测波动很大。我以后再也不写重载运算符的矩阵快速幂了!!一定要写在结构体里面直接更改。
66.我以后再多测不清空我就是狗!!(尤其是链式前向星不知道cnt=0!!!就因为这个连炸两题
67.unsigned int 和 int 比较的时候会把 int 强制转换为 unsigned int,所以如果调用vector等类型的size时,它会返回 unsigned int,如果比较符号另一边是负数了,也会给它强制转成uint!!所以有负数的时候一定要写(int)s.size()
68.double的有效数字约15位,long double约18位,感觉double快到炸精度的边界了就要用long double!!另外,浮点数一点点精度问题都可能引发堆积效应,尤其是在反复使用一个fft数组的时候,适时要把虚数部清零!!
69.二分判边界的时候,记得用原来的l,r,而不是当前l,r!!直接用l,r是当前的,不是边界!!!(一个坑掉n次
70.取模不完全!!!实在不行我也写成add/del/mul()的形式算了。。。。。。
71.最近一直在写数学专题所导致的:死活不记得写mu[1]=1!!!
72.很多时候在过程中会影响原来的变量,因此在判断条件的时候务必注意是过程中还是原来的!!!!
73.路径是不经过重复的点!!!
74.以后模拟赛先认真读题,再认真手玩样例。为什么每次题意和我看的都不一样?!
75.技巧:可删堆,用a,b两个堆,分别表示插入和删除的操作。当我们要取出堆顶元素时,可以比较两者的堆顶。若两者相同,则弹出两者。否则a的堆顶元素就是真正的堆顶元素。
76.trick:找到出现频率至少为 \(a%\) 的数,就是每次在区间中删掉 \(\lfloor \frac{1}{a%} \rfloor +1\) 个不同的数,这样,出现频率至少为 \(a%\) 的数一定被保留下来了。
77.猫树:很类似cdq分治的一种结构,具体就是从每个分治中心向两边预处理东西,查询的时候查找区间左右端点的lca处就可以前后拼接求得答案。
78.对于生成包含某串子串的字符串问题,可以直接状压可能为开头的位置。(启示是,dp设计的状态只要能保证不重不漏就行,或者说,对于每种转移不会产生歧义)
79.摩尔投票:一个用于寻找数组中出现次数超过一半的数的方法。每次设一个候选数并把它的cnt设为1,如果下一个遇到的数和当前数相同cnt++,不同cnt--,如果cnt=0,则设当前数为候选数。
80.可行性dp优化方式->去掉一维后改为数值dp。
81.转化补集/正难则反/时间倒流都是很好的手法。另外思考问题时不妨先减少限制条件,简化题意,从简到繁,一部分一部分去思考。
82.什么时候才能在考场上想起来 \(E(x)=\sum_i P(x\geq i)\) 的转化呢?
83.什么时候才能发现dp实际上需要记录的状态很少,从而想到哈希优化dp状态设计呢?
84.经典trick是,把n划分为若干正整数之和的方案数是 \(3^{\frac{n}{3}}\) 级别的。
85.我真的不是很理解为什么过一段时间就会把树上倍增写错,还几把每次都有新鲜的错法。
86.经典trick之我又想不起来——求积的形式可以转化为求满足某种条件的数组的个数。从而通过枚举这个数组的某些信息来统计答案。
87.驻波真的不会做构造/结论题。感觉大概是找到一个充分或必要条件,然后猜测它是充要条件(((一定要把每一点思路都写到草稿纸上!!!相信打表的力量
88.与枚举子集相关的枚举超集,只需要略加改动。
for(int t=s;~t;t=(t-1)&s)->for(int s=t;s<(1<<n);s=(s+1)|t)
89.queue预留空间很大,很容易mle。
90.计算了空间没问题,但还是mle:考虑把dfs换成bfs或者循环之类的。
91.dilworth定理(最长反链=最小链覆盖)可以用来刻画LIS。或理解为LIS的分层。

浙公网安备 33010602011771号