• 其他-杂乱总结


    精度

    计算 x 的位数的代码是 int bit = log(x + 0.5) / log(10) + 1; 或者 int bit = ceil(log(x) / log(10)) + 1 。即先算出 double 类型的答案,向上取整后 +1 ,再存到 int 类型里。

    如果错写成 log(x) / log(10) + 1,对于 x = 1000,(在我测试的电脑上)会计算出 3,而正确结果是 4 。

    紫书里面提到了,对某个数开根要写成 int tmp = sqrt(x + 0.5) ,否则有精度差。

    就是这两个地方,注意写的时候 +0.5 来避免类似 x.99999828 的数被截尾算作 x (即避免精度差)。

    vector 存边

    最常见的边遍历

    vector<edge>::iterator it;
    for (it = G[x].begin(); it != G[x].end(); ++it) {
    	//...
    }
    

    有时候会写成

    for (i = G[x].size() - 1; i >= 0; --i) {
    	//...
    }
    

    注意这里 i 必须是 int ,如果 i 是 long long 就必须写成 i = (long long)G[x].size() - 1,否则会出些奇怪的错误。所以最好直接给 i 开 int 。

    可以测试一下:

    int tmp = 5;
    printf("%lld", tmp - 1);
    

    结果不是 4 。

    printf("%lld", 5 - 1);
    

    结果也不是 4 。

    upd: 用笔记本试了下,能跑出 4 ,但是机房电脑不会。反正避免就是了。

    fread

    甚至强过头文件优化(雾)。使用方法同 getchar() 函数。

    char buf[1 << 20], *p1, *p2;
    inline char void gc()
    {
    	return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin), p1 == p2) ? EOF : *p1++;
    }
    

    next 数组

    $ k = 0, 1, 2, 3 $ 分别表示上下左右,然后希望找一个对应关系 f(上) = 下, f(左) = 右,反过来也是。这样的话这个对应关系 (f(k) = k xor 1)

    求区间严格(k) 大值

    正解是树套树,裸题有“二逼平衡树”。但是有的问题 (k) 比较小,直接线段树维护就可以了。假设左区间前 (k) 大值分别是 (a_1,a_2,a_3...) ,右边是 (b_1,b_2,b_3...) 。合并后当前区间的前 (k) 大值是 (x_1,x_2,x_3...)
    考虑求 (x_i) ,枚举 (a_1,...,a_i)(b_1,...,b_i) 。如果 (a_j < x_{i-1} left(1 leq j leq i ight)) ,就用这个 (a_j) 去更新 (x_i) 。对 (b_j) 同理。一次合并的时间复杂度是 (O(k^2))(k) 小的话就可以视为常数,所以基本上不会破坏线段树的时间复杂度。

    树上路径求交

    求出两条路径的 (lca) 记为 (g, h) ,如果

    • 深度相同, (g e h) 则无交集, (g = h) 则有交集。
    • 深度不同,用深度较大的 (lca) 作为 (t) ,假设另外一条路径 ((x, y)) ,则 (t) 应在 ((x, y)) 上才有交集,即:

    [lca(t,x) = t igvee lca(t,y) = t ]

    当两条树上路径有交集时,画图并且分 (5) 类情况(考虑根的位置)可知,((a, b))((c, d)) 这两对树上路径的交的两端是 (lca(a,c), lca(a,d), lca(b,c), lca(b,d)) 这四个点里,深度最大的两个点。

    树上 LCA 关联的点

    有时候边权化点权会用到,肯定可以算出深度之后重新向上跳,不过树剖的时候也可以这么搞:

        while (Top[a] != Top[b]) {
            if (Depth[Top[a]] < Depth[Top[b]]) {
                b = Dad[xb = Top[b]];
            } else {
                a = Dad[xa = Top[a]];
            }
        }
        if (Depth[xa] == Depth[lca] + 1)
            insert(xa);
        if (Depth[xb] == Depth[lca] + 1)
            insert(xb);
        if (a != b)
            insert(Heavy_son[lca]);
    

    树上换根对子树影响

    假设原树的根是 (x) ,新根是 (y) 。讨论 (t) 在新树中的子树。

    • 如果 (y) 就是 (t)(t) 在新树中的子树就是整棵树。
    • 如果 (y) 在原树中 (t) 的子树外,那么新树中 (t) 的子树仍然是原树中它的子树。
    • 如果 (y) 在原树中 (t) 的子树内,设 (t)(y)(x) 的路径上的儿子是 (g) ,而原树中 (g) 的子树是 (s) ,那么新树中 (t) 的子树是整棵树除去 (s) 这部分。

    二分查找

    STL 里有的容器(比如 set 和 map)自带 lower_bound(),这是因为这些容器有一些非常优秀的结构,用 a.lower_bound(val) 可以利用到这些结构。因此这样做的速度远快于单纯的二分查找 lower_bound(a.begin(), a.end(), val)

    图论题小细节

    • 无向边用链式前向星存的时候记得开两倍边的空间。
    • Tarjan 缩点之后用 Topsort 搞入度之类的东西的时候,记得考虑自环和重边——自环往往对入度并不应该有贡献
    • 看清无向图还是有向图(因为这个爆零两次了)

    子集求和

    从 nodgd 的一场训练赛里学到的新技能。就是 (O(n cdot 2^n))(f(S) = sum f(T) (T subsetneq S))

    实际上就是一个多维前缀和。比如说,二维前缀和可以看做先算每行的前缀和,然后再算新的矩阵中每列的前缀和。同理,多维前缀和依次算每一维(代码中的 i )的前缀和。

    听说有个叫快速莫比乌斯变换的东西 Orz 。

    	for (int i = 0; i < M; ++i)
    		for (int j = 0; j ^ FUL; ++j)
    			if (j >> i & 1)
    				f[j] += f[j ^ 1 << i];
    

    换行符

    爆零警告: Linux 下换行符是 ' ' 。不要用“是换行符”作为输入结束的标识,而应该用“不是合法字符”

    C++11 新特性(注意 NOIp 不能用,省选不一定能用

    mt19937

    据说可以得到 unsigned int 范围内的随机数。注意 time(NULL) 在对拍时由于程序跑得太快,可能会使两个程序有相同的 seed 。 chrono 库提供了更精确的计时函数,所以基本上不会出现上面说的情况。

    #include <cstdio>
    #include <chrono>
    #include <random>
    
    using namespace std;
    
    mt19937 mand(chrono::steady_clock::now().time_since_epoch().count());
    
    int main(){
        printf("%u
    ", mand());
        return 0;
    }
    

    tuple

    制作 tuple

    tuple<int, int, int, int> tp = make_tuple(a, b, c, d);
    

    解开 tuple:

    int a, b, c;
    tie(a, b, c, ignore) = tp;
    

    或者

    int a, b, c;
    a = tp.get<0>();//等价于 a = get<0>(tp);
    b = tp.get<1>();
    c = tp.get<2>();
    

    编译命令

    • 本地有庞大的递归调用需要加 -Wl,--stack=一个很大的数 ,不然并查集都可能会爆栈。
    • 开启 C++11 需要加 -std=c++11
    • Lemon 在 32 位下评测时需要加 -m32 以生成 32 位代码。

    链式前向星的清空

    只需要清空 first 数组就可以了,有的时候这会大大影响程序效率。

    主席树

    记住每次新建,写

    	if (!p)
    		new_node(p);
    

    的后果是调试到死。

  • 相关阅读:
    力扣算法题—048旋转图像
    力扣算法题—047全排列2
    力扣算法题—046全排列
    力扣算法题—045跳跃游戏二
    数据结构【排序】—排序算法大集合
    数据结构【查找】—B树
    第一百四十四天 how can I 坚持
    第一百四十一/二/三天 how can I 坚持
    第一百四十天 how can I坚持
    第一百三十九天 how can I 坚持
  • 原文地址:https://www.cnblogs.com/ghcred/p/9494337.html
Copyright © 2020-2023  润新知