• 【每天一个爆零小技巧】个人用的程序小技巧及其他_自用


    常数小技巧

    • 不论整数,浮点数,除法是真的慢
    • 整数取模尽量避免,这也是大部分程序的卡常关键(比如20200610的T1,pcf就被两次取模卡掉了40分)。
    • 要尽可能让几次对一个数组的调用地址距离近。比如(f_{i,j,k})中,大部分运算是在相同的(i,k)下做的,那么我们应该存为f[i][k][j]。(upd20201029:这个东西真的还挺有用的,APIO2013机器人就是拿这个过的)(一个更好的例子:当我们定义ST表的时候,大部分时候我们应该定义ST[21][MAXN+1],实测很有效果)。
    • 一个优秀的快读快输很有必要(但是事实上很多人的快读快输是错的,不能读-2147483648)
    template<typename T> void Read(T &cn)
    {
    	char c; int sig = 1;
    	while(!isdigit(c = getchar())) if(c == '-') sig = 0;
    	if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
    	else    {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
    }
    template<typename T> void Write(T cn)
    {
    	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
    	if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
    	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
    	while(wei--)putchar(cm%10+48),cm/=10;
    	putchar(cx+48);
    }
    
    • 这里是一个我在某次训练里手打的fread,fwrite的模板,搭配上面的快读快输使用,感觉还挺好的。(程序开始的时候需要调用pre_in(),pre_out();结束时需要调用end_out();)
    #define MAXNUM 1000000
    char tmp_in[MAXNUM], tmp_out[MAXNUM];
    int tmplen_in, tmplen_out;
    void pre_in() {tmplen_in = MAXNUM; } 
    int getone() {if(tmplen_in == MAXNUM) fread(tmp_in,1,MAXNUM,stdin), tmplen_in = 0; return tmp_in[tmplen_in++]; }
    void pre_out() {tmplen_out = 0; }
    void end_out() {fwrite(tmp_out,1,tmplen_out,stdout); }
    void putone(char cn) {if(tmplen_out == MAXNUM) fwrite(tmp_out,1,MAXNUM,stdout), tmplen_out = 0; tmp_out[tmplen_out++] = cn; }
    
    • 一个优秀的快速幂也比较有用
    LL ksm(LL cn, LL cm) {LL ans = 1; while(cm) ans = 1ll*ans*(1+(cn-1)*(cm&1))%MOD, cn = cn*cn%MOD, cm = cm>>=1; return ans; }
    
    • 快速乘的正解是__int128
    • 循环展开
      • 循环展开是最后的计策
      • 循环展开的本质是缓存优化。可以参考APIO2019的课件。所以如果展开4层还没有什么效果,那基本是没救了。(除了矩乘)
      • 一份比较好的示例代码
    LL ans1 = 0, ans2 = 0;
    int i = 1;
    for(;i+3<=n;i+=4) {ans1 = ans1+(a[i]+a[i+1]); ans2 = ans2+(a[i+2]+a[i+3]); }
    for(;i<=n;i++) ans1 = ans1+a[i];
    ans1 = (ans1+ans2)%MOD;
    
    • memset,最好算好要用前多少位,然后精确清零
    • 正确分析好程序的复杂度瓶颈,然后只在瓶颈上优化,除非其它地方优化起来太方便了,可以顺手搞一下。毕竟卡常的时候是很容易fst的。
    • memcpy也挺好用的。memcpy(destiny, source, sizeof(unit)*length)。
    • 分块这种东西啊,T与不T常常就在常数之间。能用一些分块的数据结构之类的,就少用整数分块(活生生的例子:20201016_T2)。
    • 在大规模处理的时候,少调用函数(比如你要回答1e7组询问,那还不赶快手动inline)。(例子:20201130_T3)

    数组

    • (2^{20})开1e5,1e6都不够。
    • 数组一定要科学清空
      • 数据千万条,清零第一条;多测不清空,爆零两行泪。
    • 分块内部开哈希,修改的时候一定要全面修改。
    • 数据范围要好好注意一下,并不是每一道题的(n)(q)都是相同的。
      • 经典节目:数组开小(少加一,或者少开两倍)
      • 要注意数据范围看对:有时候最大的数据范围会在数据范围那里的第一行,而不是最后一行————最后一行有可能是倒数第二个Subtask。
      • 很多时候主席树开40MAXN没有问题,甚至可以说比较安全。但是我发现这还是有爆空间的风险。(我有一天的T1就是因为空间限制128MB,n大概是2e5,主席树一个节点里记录了四个元素,开了40MAXN,然后就光荣爆炸了)

    其他细节

    • 爆int和爆long long是真的自闭。我今天调题,有一大半的时间都是因为爆了int而自闭。+1+1
      • 特别要注意的是计算几何和min25筛。计算几何的问题主要是存在叉积,而min25筛的问题是n都会爆int,直接n*n就爆long long了。
    • 名称相似而且作用相似的数组,一定要第一遍写对,并在静态查错的时候重点关注。最好名字要容易区分一点。
    • 未定义行为检测真有用。-fsanitize=undefined
    • 边界情况好好判啊。写完之后哪怕多花五分钟搞点边界数据卡一卡也好。
    • 好好判无解之类的,千万不要默认他永远有解。
    • 数据分治,如果要直接在主程序里return Sub1::main(); 那么Sub1::main()的返回值一定要是0。可以用未定义行为检测(-fsanitize=undefined)/-Wall/windows环境里的dev-c++来避免。
    • 修改边权点权的时候,不仅要在维护的数据结构中改,还必须改一下原始数组。
    • 读入优化好是好,就是判到EOF的时候容易出事。

    语法碎碎念

    • c++中,class和struct的效率差不多。事实上struct的实现就是所有元素都是public的class。class的功能更强大,相应地,struct的运用更方便。用class还是struct完全是个人习惯问题。
    • 我觉得struct和class的并存的原因,一个是兼容标准问题,一个是用法习惯问题。如果是一个五元组,显然用struct更符合“习惯”;而如果是一个功能比较复杂的东西,比如lct,或许使用class会更顺眼。

    算法碎碎念

    杂项

    • 数论分块真香!
      • Update 2020.06.11:他太蠢了,我昨天的T2,加个数论分块就wa了,一切都是因为我没有把一个每次都乘的数字做幂。但事实上那个数论分块不加这道题也能过。
    • 模块化真香!
    • 斜率优化判交点,如果直接用一些解析几何或者计算几何的方法(((k_1-k_2)(b_3-b_2)<(k_2-k_3)(b_2-b_1))这种),很有可能会爆精度、爆long long,然后让你调到自闭。
    • 分块真香。如果每一块中的操作极其雷同,甚至可以前缀和。

    计数

    • 可以一上来就莽式子,反正都是一通爆乘一通爆加,写就完事儿了。
    • 考虑用dp来计数。
    • 但是千万不能忘了容斥这个工具。
    • 反演也挺香的。特别是二项式反演(有关选择,但是其中的组合数学部分要慎重考虑)和莫比乌斯反演(有关gcd,约数)。
    • 很多时候不能一下子全都推完。这时候就要一步一步慢慢来,先用暴力写出来,看看这个式子到目前为止对不对。
    • 普通的题目是推式子推到最后才要考虑套上多项式的。当然也有一上来就莽的。比如仙人掌计数。
    • 如果要维护形式幂级数,那么也可以维护点值,最后插值插回去。
    • 有些东西写出式子来不一定能看出怎么优化。写到程序里能更清晰一点。

    贪心构造

    • 第一种思路:发现自己如果知道一段是有解的,可以用题目能接受的复杂度求出来。那就继续深刻理解判断有解的条件。尝试简化/形式化/用推理弱化。
    • 第二种思路:发现自己如果知道一段是有解的,可以用题目能接受的复杂度求出来。考虑优化求解的方式,用这个方式来判断一个区间有无解。

    关于网络流

    • 建图:
      • 如果是网格图,黑白染色是一个非常好的思路。
      • 如果一个人要用两种东西,那么把一种东西放在前面,一种放在后面跑费用流也是不错的。
    • 费用流
      • 我们通常跑的都是最小费用最大流。这时我们通常是要求流满的情况下费用尽可能小。但是有可能会有这种情况:我们出现了负权,每次流只是为了赚钱。我们也不一定要流满。此时就应该每次流都判断一下,如果开始亏钱就退出。参见20200929T2。

    关于数据结构

    • 我人傻了。我维护区间加区间乘的lct,竟然在pdown的时候搞反了乘和加的顺序(我一般都是先乘再加,因为这样处理tag的时候比较自然,但是我pdown的时候竟然先加再乘了)。
    • KD-Tree的更新要很注意,不要随便更新0(当然也可以对0进行特殊处理使得更新他没用,比如一个区间最小值,0上设为INF。)其他数据结构同理。

    数字收集

    ntt模数(少见)

    104857601 = (25 imes 2^{22}+1) primitive root = 3 max ntt = 4194304
    2025848833 = (483 imes 2^{22}+1) primitive root = 10 max ntt = 4194304
    377487361 = (45 imes 2^{23}+1) primitive root = 7 max ntt = 8388608
    2088763393 = (249 imes 2^{23}+1) primitive root = 5 max ntt = 8388608
    754974721 = (45 imes 2^{24}+1) primitive root = 11 max ntt = 16777216
    2130706433 = (127 imes 2^{24}+1) primitive root = 3 max ntt = 16777216
    167772161 = (5 imes 2^{25}+1) primitive root = 3 max ntt = 33554432
    2113929217 = (63 imes 2^{25}+1) primitive root = 5 max ntt = 33554432
    469762049 = (7 imes 2^{26}+1) primitive root = 3 max ntt = 67108864
    1811939329 = (27 imes 2^{26}+1) primitive root = 13 max ntt = 67108864
    2013265921 = (15 imes 2^{27}+1) primitive root = 31 max ntt = 134217728

  • 相关阅读:
    window7访问虚拟机ubuntu中的mysql
    photo sphere viewer使用图像数据替代路径来生成全景图
    pgmagick,pil不保存图片并且获取图片二进制数据记录
    flask_admin model官方文档学习
    python pip更换下载源(转)
    flask admin学习记录
    mongodb权限管理(转)
    vmware三种网络连接模式区别
    使用VLC推送TS流(纯图版)
    【转】RTMP/RTP/RTSP/RTCP协议对比与区别介绍
  • 原文地址:https://www.cnblogs.com/czyarl/p/12985304.html
Copyright © 2020-2023  润新知