注意事项

  1. 有关1<<64。测试:
using namespace std;
#include <iostream>
int main(){
	cout<<(1<<64)<<endl;
	cout<<(1ll<<64)<<endl;
	cout<<(1ull<<64)<<endl;
	
	int k=64;
	cout<<(1<<k)<<endl;
	cout<<(1ll<<k)<<endl;
	cout<<(1ull<<k)<<endl;
	return 0;
}

结果:

0
0
0
1
1
1

所以不要随便左移64位。

  1. 有关四舍五入:
using namespace std;
#include <cstdio>
#include <cmath>
int main(){
	double a=0.5,b=0.5+1e-16,c=0.5+1e-17,d=0.5-1e-16;
	printf("%.0lf\n",a);
	printf("%.0lf\n",b);
	printf("%.0lf\n",c);
	printf("%.0lf\n",d);
	printf("%lf\n",round(a));
	printf("%lf\n",round(b));
	printf("%lf\n",round(c));
	printf("%lf\n",round(d));
	return 0;
}

结果:

0
1
0
0
1.000000
1.000000
1.000000
0.000000

可以发现printf在刚好是0.5时四舍五入输出0round符合人类习惯。

  1. 如果定义某个函数,把它当过程使用,但是类型不是void而且函数中没有返回值,会出问题。
    在洛谷、LOJ、Atcoder上都出现过问题。
    没有用CCF的评测机试过。

  2. C++中的listinsert(iter,x)表示将x插入到iter

  3. 以后不要一个个写头文件了,都用万能库#include <bits/stdc++.h>

  4. 分块的时候注意:

const int N=100005;
const int B=500;
//int b[N/B];错误
int b[N/B+5];//正确

都明白。

  1. vector等STL中调用size()返回的是size_t类型,是个unsigned类型。

    所以用v.size()-1这样的东西的时候请注意。

  2. 平面上有一堆直线,现在有个扫描线从左到右扫,维护截距的大小关系。有种\(O(n^2)\)的做法是计算出两两之间大小关系改变的时间,排序,扫的过程中如果到达那个时间则交换。然而在多条直线交于一点的时候可能会出锅,所以在交换之前先判断一下相对位置是否已经改变。

  3. 在c++98中,假如结构体定义了初始化函数,则初始化的时候直接用大括号赋值且没有强制转换类型会编译错误。

    struct com{
        int x,y;
        com(int _x=0,int _y=0){x=_x,y=_y;}
    };
    com a={1,0};//CE
    com a=(com){1,0};
    com a(1,0);
    

    如果没有定义初始化函数则不会出现这样的情况。

  4. y0,y1,yn,j0,j1,jn不要用作全局变量名。

  5. 在使用两个模数交替计算时(比如多项式相乘,模数不是NTT模数,结果不会超过long long ,用一个特大的模数\(29*2^{57}+1\)来跑NTT,最后的结果再用那个模数取模),注意切换模数之前,把数值调回正数。

  6. FFT在\(n=1000\)\(a_i\le 10^6\)的时候的就已经没有精度保障了。

  7. 整数三分模板:

    while (l<r){
    	ll mid1=(l*2+r)/3,mid2=(r*2+l+2)/3;
    	ll v1=calc(mid1),v2=calc(mid2);
    	if (v1>v2)
    		l=mid1+1;
    	else
    		r=mid2-1;
    }
    

    (最好背下来)

  8. 注意&,^,|>,<,>=,<=,==,!=的优先级,比较运算符优先。(不包括<<,>>

  9. 在写wqs二分时,最好给个第二关键字安排顺序。如果第一关键字相同时第二关键字大的优先,那么二分时:

    while (l<=r){
    	int mid=l+r>>1;
    	if (calc(mid).se>=C)//看这里
    		r=(res=mid)-1;
    	else
    		l=mid+1;
    }
    

    否则反过来。

  10. 就算空间是\(O(n)\)的也不要忘了检查一下空间。(如果是\(10^6\)级别)

  11. 变量名end在noi linux上会挂。

  12. 如果线段树这么打:

    void modify(...,int k,int l,int r){
    	if (st<=l && r<=en){
        //或者if (l==r){
            ...
           	upd(k);//看这里
            return;
        }
        ...
    }
    

    要么把线段树开到\(8\)倍,要么特判一下叶子节点。

  13. 多项式求逆中,假设要从长度\(i\)扩展到\(2i\),记得算NTT时大小要开到\(4i\)

  14. 循环上界+1超过int范围时,要注意换成long long。

  15. zkw费用流不能跑负边(不只是负环)。上下界最小费用最大流需要先用超级源汇跑,再用原来的源汇跑。在这个过程中就会产生负边,于是zkw费用流就会错。

  16. 计算几何中:如果以某个点为中心,对其它点极角排序,然后对排序后相邻点进行操作,此时注意相邻点极角差大于\(\pi\)的情况。

  17. 后缀自动机中,点数最多为\(2n-1\),边数最多为\(3n-4\)

  18. 双哈希比较两个值是否相等的时候,算一个模数比较一个模数。因为算一个模数就已经发现不相等的概率还是很大的,这样就大概率可以少一次运算(因为有取模所以很慢)。

  19. 写多项式系列的时候,如果不注意,开的临时数组很多,注意空间(可以假装是\(O(n\lg n)\)空间)。

  20. 在FFT的multi(c,a,b,n)中,如果\(n\)表示项数为\(x^0\dots x^n\),那么在后面进行模\(x^n\)意义操作的时候,记得用\(n-1\)。这个在倍增(求逆,求exp,牛顿迭代)的时候很致命,可能让空间加倍。

  21. 在dinic中,BFS时如果遍历到\(T\)就直接退出,这样有时候会快很多。lyl-sap在遍历到\(T\)之前都在不断迭代加深,可以直接BFS算出遇到T之前迭代加深之后的状态(dis,gap,cur),再进行后面的操作。这个BFS的过程也可以用前面那个优化。

  22. 如果开了O2,注意一下O2对程序某些不寻常的优化(比如说某个循环对外面的变量没有影响,也没有输出,那么它可能会直接把循环跳过。在注释掉输出看看程序运行时间的时候可能要注意?)

  23. 写凸包如果采取极角排序的形式(见注释):

    1. 因为零向量(由重复点产生)是个奇怪的东西,所以应该要避免它出现。
    2. 因为三点共线时,用叉积只会显示\(0\)

    为了解决以上两个问题,我精心尝试研究出了以下代码。

    bool operator<(DOT a,DOT b){return a.x<b.x || a.x==b.x && a.y<b.y;}
    bool cmparc(DOT a,DOT b){return cro(a,b)>0 || cro(a,b)==0 && a.len2()<b.len2();}
    //如果没有第二关键字,可能从基准点伸出的两条向量(即两点和基准点三点共线),长的被短的弹掉。
    //如果两点和基准点没有三点共线,已经通过极角将其区分,不会产生问题。
    //因此第二关键字换成a.x<b.x似乎也可以?
    //第二关键字用<,是配合下面第一处弹栈的<=使用。
    void getcon(vector<DOT> &w){
    	DOT O=w[0];//基准点
    	for (int i=1;i<w.size();++i)
    		O=min(O,w[i]);
    	qa.clear();
    	for (int i=0;i<w.size();++i)
    		if (w[i].x!=O.x || w[i].y!=O.y)//保证基准点不重
    			qa.push_back(w[i]-O);
    	sort(qa.begin(),qa.end(),cmparc);
    	static DOT st[N];
    	int tp;
    	st[tp=1]=DOT(0,0);
    	for (int i=0;i<qa.size();++i){
    		while (tp>1 && cro(st[tp]-st[tp-1],qa[i]-st[tp-1])<=0)
                //注意<=,可防重复点,如果st[tp]==qa[i],则会被弹掉
    			--tp;
    		st[++tp]=qa[i];
    	}
    	while (tp>1 && cro(st[tp]-st[tp-1],DOT(0,0)-st[tp-1])<0)
       //注意<。因为保证基准点不重,所以此时不用防重复点。必须用<是因为可能会出现只有两个点的情况,如果用<=会只保留基准点
    		--tp;
    	w.clear();
    	for (int i=1;i<=tp;++i)
    		w.push_back(st[i]+O);
    }
    
  24. 在noilinux中按了ctrl+alt+f11会黑屏,出现这种情况用ctrl+F7。

  25. set中的iter.erase()返回值(下一个位置)只有C++11支持。

  26. 写实数二分的时候,如果后面要计算方案,最好二分之后check(r)(或check(l))。

  27. unique中如果要加cmp函数,注意是判断是否相等而不是比大小。

  28. unorded_set如果用一些其它的类型可能要首先哈希函数(intlong long不用,其它没试过)。

  29. 竞赛图中没有大小为\(2\)的强联通分量……

  30. random_shuffle后面可以加随机函数。写成int myrand(int i){return rand()%i;}这样。

  31. 递归的时候,那些看起来在递归前已经释放,或在递归后才定义的变量,包括函数返回值(min,max,不过我不知道为什么运算符没事),可能一直占空间直到退栈。看这题。另外开O2可能导致空间扩大。

  32. '??-'是组合字符(类似于\n这种),等于'~'

  33. 自己写的spj不要写错(如果可以的话要用spj测测样例?)

  34. 用全文替换的时候要注意一下自己有没有替换掉不该替换的东西(比如\n中的n,"r"中的r)。

posted @ 2020-11-08 21:14  jz_597  阅读(195)  评论(0编辑  收藏  举报