考场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().swap(v)可以释放v的内存。

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的分层。

posted @ 2024-10-06 16:11  theWeeds'Defense  阅读(32)  评论(0)    收藏  举报