Forever Young

「干货」挂分小技巧

写在前面

下面的内容中每一个挂分小技巧:
第一行是指出了错误,第二行及以后说出了正确做法(可能没有),
如有不是的会特别指出,请注意。

常见的挂分小技巧

  • \(\max(a, b)\) 或者 \(\min(a, b)\) 写成 \((a, b)\),这样会取后面的数,即 \(b\)
  • 在结构体里开数组开的太大。
    这种情况下即使没爆空间也会出现许多奇怪的错误。
  • 数组的下标减出负数导致 \(\text{RE}\)
    请一定注意这个地方,一定要加一个判断,数组下标一般不能是负数!(除非你来这里
  • 使用了结构体,导致内存不连续访问被卡常。
  • 数组嵌套过多被卡常。
  • 输出 ull 但不知道 printf 对应的占位符,宁肯瞎猜也不用 cout。
    这种情况请使用 cout。
  • 比较两堆数的乘积大小直接乘了起来导致爆掉
    应取 log 函数进行比较。(清北国庆刷题营Day3T1、洛谷P4370组合数问题)
  • 设置最大值时比给出数据的最大值还要小。
    设置最大值时注意数据范围。
  • 复制粘贴代码后没有修改充分导致出现错误。
    这种错误的解决方法就是:认识检查或者直接重写而不是复制粘贴。
  • 做完题之后不检查。
    我就是生动的例子,只过样例不可靠,样例一般都是精心设计好的,一定要自己多造几组数据。
    检查很有用,但是也不保证检查一定会查出错,不保证不会改错。
  • 在使用 \(\text{sqrt()}\) 函数 或 \(\text{pow()}\) 函数 时没有进行类型转换。
    注意 \(\text{sqrt()}\) 函数的返回值 和 \(\text{pow()}\) 函数的返回值为浮点数。
  • 滚动数组忘记清空。
    滚动数组不清空,爆零两行泪。在 DP 中经常会用到滚动数组来降低空间复杂度,为了避免意外的错误发生,请记得清空已经用过的滚动数组。
  • 全局变量和快读的局部变量相同都会读入一些奇奇怪怪的东西。
    这个我好想没遇见过……不过学弟都说了就放上来吧。
  • printf 里面 %llu 写成 %ull。
  • 读入字符用 scanf。
    用 scanf 中的 %c 来读多个用空格隔开的字符时,它会去读空格。这个时候我们可以这么写:
    char s;
    //在 %c 前加一个空格
    scanf(" %c", &s);
    
  • 随时计算 for 循环的上界。
    for循环的上界不要随时计算, 复杂度可能爆炸, 类似于 i<=sqrt(q),i<=strlen(s)
  • 位运算不注意优先级导致运算出错。
    位运算的运算优先级很低,所以在不了解优先级的前提下,建议位运算加括号。
  • 特别指出,此处为正确做法)注意实数二分的精度问题, 实数输出的精度问题。
  • 特别指出,此处为正确做法)在struct 中, 写重载运算符时注意格式。
  • 特别指出,此处为正确做法)在操作时注意数组边界, 包括枚举时超出定义大小, 和枚举的部分应不应该有初值。

取模中的挂分小技巧

  • 模数取错。
  • 不随时取模。
    注意取模,看好两个数相乘的结果是否会爆出你所定义的类型。
    当然也不是取模越多越好,因为取模本身比较慢,所以应该在合适的地方取模。
  • 特别指出,此处为正确做法)在取模时注意负数的取模。

本质为手误的挂分小技巧

  • \(\text{in}\)\(\text{out}\) 文件写错,正确写法(根据题目而定):
    freopen("a.in", "r", stdin);
    freopen("a.out" ,"w", stdout);
    
  • 莫名其妙在代码里多加了几个符号。
  • 奇奇怪怪的手误,比如:变量写错,\(i\)\(j\) 写反,\(f[y][i]\) 写成 \(f[y][y]\)\(s=read(),t=read()\) 写成 \(s = read(), s = read()\)
    规范写法, 能写 k>0 就不写 k, 循环变量要多变,不要只写 ijk, 冗余信息可以减少出错。

本质为脑抽的挂分小技巧

  • 不写读入。
  • 数组开小。
  • 快读把字符写成整形。
  • 自作聪明,不看题目要求。
    认真读题啊,先把题目多读几遍圈画出重要信息再做
    更小的数组,更多的挂分。
  • 该开 \(\text{long long}\) 的题没开 \(\text{long long}\)
  • 多测不清空,爆零两行泪。
    多组数据记得清空,注意清空要完全
  • 炸空间,或者空间开小,有的题空间开小不一定会 \(\text{RE}\),还有可能会 \(\text{WA}\)
  • 不好好看看数据范围。
    数据范围十分重要!直接决定了你的分数!
    \(2^{31}\)\(\text{0x3f3f3f3f}\) 大!
  • 在有返回值的函数中不加 \(return\)
  • 不给局部变量赋初值。
    记得局部变量变量赋初值,否则会死的很惨,就像下面这样,一个代码四个答案。
  • 自以为开了 longlong 但在一处微小的地方没开 longlong。
    认真检查,杜绝发生。
  • 写有 elseif 语句时忘记写 else

图论中的挂分小技巧

普通

  • 带权边建成无权边。
  • 双向边建成两条一样的单向边。
    不要求快,要求准,用好自己的手和眼。

倍增求LCA

  • 倍增lca 把根节点的深度设成 0
    不是很常见,这样无法和跳到 NULL 区分。

最短路

  • 单向边建成双向边
    单向边,单向边!
  • 有向图建成无向图
    有向图,有向图!

建树

  • 读入一棵树的时候读入了 \(n\) 条边。
    一棵树是一张有 \(n\) 个点 \(n-1\) 条边的连通图。

网络流

  • 建边从奇数开始建。
    建边要从偶数边开始建,这样 \(\text{i^1}\) 才能找到 \(\text i\) 的反向边。
  • 建了编号为 \(0\) 的边但是没初始化 \(\text{head}\) 数组为负值。
    如果要建编号为 \(0\) 的边 一定记得初始化 \(\text{head}\) 数组为 \(-1\)
  • 不判断容量为 \(0\) 的边是否为起点 \(S\)
    输出方案时容量为 \(0\) 的边不一定是用过的边,还有可能是从起点连过来的边的反向边,所以输出方案时要判断这种情况。

矩阵中的挂分小技巧

  • 矩阵不写初始化。
    矩阵记得初始化,我的写法如下:
    struct matrix { 
      int a[A][A];
      matrix() { memset(a, 0, sizeof(a)); } 
      void init() { for (int i = 1; i <= n; i++) a[i][i] = 1; }
    };
    
  • 将矩阵交换后相乘。
    矩阵乘法不满足交换律,请不要这样使用

数据结构中的挂分小技巧

不知道放到哪儿

  • 一系列需要动态开点的数据结构(主席树,SAM)没有继承之前状态的信息。

单调栈

  • 初始化单调栈,\(set\) 中没有加入极值。

主席树

  • 吝啬空间,空间开小。
    一定不能吝啬空间,开 \(30\) 倍左右?
  • 在主席树中,if (k <= tmp) query(t[x].l, t[y].l, t[lca].l, t[fa_lca].l, l, mid, k); 中的 k<=tmp 写成 k>=tmp
  • \(\text{build}\) 函数中,忘记加 \(now\),答案没错,但是程序运行十分缓慢。
    void build(int &now, int l, int r) {
      t[now = ++tcnt].sum = 0;
      //debug:t[now = ++tcnt].sum = 0写成t[++tcnt].sum = 0;
      //忘记加now导致程序运行速度十分缓慢 
      if (l == r) return;
      int mid = (l + r) >> 1;
      build(t[now].l, l, mid), build(t[now].r, mid + 1, r);
    }
    

线段树

  • \(\text{query,update}\) 函数中的 \(l,r\) 写成 \(l,mid\)\(mid+1,r\)
    如果是用结构体记录了每个区间的左右端点,那么函数中的 \(l,r\) 指的是要查询或要修改的区间,这个区间的端点应该是不变的。
  • 吝啬空间,空间开小
    至少开 \(4\) 倍空间,最好不止 \(4\) 倍,特别是当 \(n\) 达到 \(5e5\) 级别时,要千万注意。
  • 权值线段树维护的值域 出现了负数,没有添加偏移量。

树链剖分

  • 混淆变量名、函数名
    因为这个\(\text{WA}\)过(比如在\(dfs1\)里调用了\(dfs2\)……)
  • 树链剖分 dfs2 中 \(top[u]=u\) 实现了暴跳
  • 跳到链顶。
    在树剖过程中跳的时候是跳到链顶的父亲节点那里,而不是跳到链顶。
  • if(dep[x] < dep[y]) swap(x, y) 写反导致TLE
  • 写树剖时线段树部分中建树用 sum[x] = a[x]
    这个就不用多说了吧……
  • 主函数里没有写两个 dfs,只写了线段树的建树
    你可能建了一棵权值全部为 0 的线段树。
  • 树剖区间取值时把原线段树 query 函数的 l,r 和 L,R 写反
    理解好每个变量的意义,不要硬记。

平衡树

  • \(\text{FHQ}\) 合并的两棵树 不是 \(\text{Split}\) 分裂获得的两棵树。

LCT

  • \(\text{LCT}\) 旋转时最后更新祖父信息。
    虽然 \(\text{splay}\) 可以这么写,但 \(\text{LCT}\) 中由于之前的操作改变了结构,写法不当会导致祖父的信息出错。

STL中的挂分小技巧

  • 特别指出,此处为正确做法)类似 堆 和 vector 的 STL 在使用的时候, 要注意是否为空

DP中的挂分小技巧

状压DP中的挂分小技巧

  • 状压DP的时候写错运算符 (fl & l)(fl && l)

背包DP

  • 背包转移时枚举顺序出错。

字符串中的挂分小技巧

(和字符串有那么一丢丢关系的的就都放这了。

普通

  • 没有注意 \(\text{char}\) 类型的上限是 \(127\),写了 'z'+6 导致爆掉。

trie

  • \(\text{trie}\) 处理二进制问题时,存储从高位到低位存 和 查询却按照低位到高位查。

数学中的挂分小技巧

(和数学有点关系的就这儿了。

  • 质因数分解漏掉 \(< \sqrt{x}\) 的至多一个质因子。
  • \(\text{exBSGS}\) 转化后指数可能为负数。
  • 一个奇怪的边界问题。
    对于 \(x\in \Z\)
    \(x\le \dfrac{a + b}{2}\iff x\le \left\lfloor\dfrac{a+b}{2}\right\rfloor\)
    \(x> \dfrac{a+b}{2}\iff x\ge \left\lfloor\dfrac{a+b}{2}\right\rfloor+1\)

应用到C++特性的挂分小技巧

  • 声明数组时赋初值。
    注意C++的特性:若在声明数组时为其赋初值,则会将整个数组遍历一边来赋值。 即使这样写: a[1e7] = {1} , 它仍然会遍历数组,并将 1~1e7-1 赋为0造成时间爆炸式浪费,以致编译超时,还会造成MLE

很淦的挂分小技巧

  • \(\text{NOIP}\) 的时候踢电源线。
    这个小技巧实在是太狠毒了,不仅会让你挂分,还会让别人挂分,真是干得漂亮太恶毒了。
  • 考试闹情绪。
  • 删库。
  • 在freopen 前边写读入。
  • 激情码码码正确率高达 100% 然而忘了 prework(预处理)。
  • 在主函数里调用 return main();

高精度中的挂分小技巧

  • 写高精高精数组开得太大
    写高精的时候,封装经常会用到 memset,高精数组开太大会导致memset时长加n倍,一定要估计好题目最大数的位数然后决定这个数组的大小

可能会导致挂分的小技巧

  • 使用 cmath 中自带的 max、min 函数
    可能会比自定义函数慢

写在最后

事无巨细,有挂分技巧就写

如果有朋友愿意提供新的挂分小技巧,可以评论或者发Q联系我,格式如下,方便整理,没有的类型可以再加上来,感激不尽!有写错的地方也请指出!

【类型】中的挂分小技巧

【内容】

感谢 \(\text{_Destiny}\) 同学提供了多条奇怪的挂分小技巧!
感谢 \(\text{LuckyBlock}\) 同学提出了一些不常见的神奇的挂分小技巧!
感谢 \(\text{xwmwr}\) 同学提出了许多不常见的挂分小技巧!
感谢 \(\text{yu__xuan}\) 同学提出了在做题过程中关于树剖的一个挂分小技巧!
感谢 \(\text{genshy}\) 大佬提出了滚动数组挂分的小技巧!
感谢 \(\text{KnightL}\) 同学提出了多个挂分小技巧!
感谢 \(\text{Aliemo}\) 同学提出了多个挂分小技巧!
感谢 \(\text{Eiffel_A}\) 同学提出了多个挂分小技巧!

(排名不分先后)

大家都强强/se

posted @ 2020-08-26 15:06  Loceaner  阅读(1571)  评论(134编辑  收藏  举报