注意事项
- 有关
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位。
- 有关四舍五入:
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
时四舍五入输出0
,round
符合人类习惯。
-
如果定义某个函数,把它当过程使用,但是类型不是
void
而且函数中没有返回值,会出问题。
在洛谷、LOJ、Atcoder上都出现过问题。
没有用CCF的评测机试过。 -
C++中的
list
中insert(iter,x)
表示将x
插入到iter
前。 -
以后不要一个个写头文件了,都用万能库
#include <bits/stdc++.h>
-
分块的时候注意:
const int N=100005;
const int B=500;
//int b[N/B];错误
int b[N/B+5];//正确
都明白。
-
vector
等STL中调用size()
返回的是size_t
类型,是个unsigned
类型。所以用
v.size()-1
这样的东西的时候请注意。 -
平面上有一堆直线,现在有个扫描线从左到右扫,维护截距的大小关系。有种\(O(n^2)\)的做法是计算出两两之间大小关系改变的时间,排序,扫的过程中如果到达那个时间则交换。然而在多条直线交于一点的时候可能会出锅,所以在交换之前先判断一下相对位置是否已经改变。
-
在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);
如果没有定义初始化函数则不会出现这样的情况。
-
y0,y1,yn,j0,j1,jn
不要用作全局变量名。 -
在使用两个模数交替计算时(比如多项式相乘,模数不是NTT模数,结果不会超过
long long
,用一个特大的模数\(29*2^{57}+1\)来跑NTT,最后的结果再用那个模数取模),注意切换模数之前,把数值调回正数。 -
FFT在\(n=1000\),\(a_i\le 10^6\)的时候的就已经没有精度保障了。
-
整数三分模板:
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; }
(最好背下来)
-
注意
&,^,|
和>,<,>=,<=,==,!=
的优先级,比较运算符优先。(不包括<<,>>
) -
在写wqs二分时,最好给个第二关键字安排顺序。如果第一关键字相同时第二关键字大的优先,那么二分时:
while (l<=r){ int mid=l+r>>1; if (calc(mid).se>=C)//看这里 r=(res=mid)-1; else l=mid+1; }
否则反过来。
-
就算空间是\(O(n)\)的也不要忘了检查一下空间。(如果是\(10^6\)级别)
-
变量名
end
在noi linux上会挂。 -
如果线段树这么打:
void modify(...,int k,int l,int r){ if (st<=l && r<=en){ //或者if (l==r){ ... upd(k);//看这里 return; } ... }
要么把线段树开到\(8\)倍,要么特判一下叶子节点。
-
多项式求逆中,假设要从长度\(i\)扩展到\(2i\),记得算NTT时大小要开到\(4i\)。
-
循环上界+1超过int范围时,要注意换成long long。
-
zkw费用流不能跑负边(不只是负环)。上下界最小费用最大流需要先用超级源汇跑,再用原来的源汇跑。在这个过程中就会产生负边,于是zkw费用流就会错。
-
计算几何中:如果以某个点为中心,对其它点极角排序,然后对排序后相邻点进行操作,此时注意相邻点极角差大于\(\pi\)的情况。
-
后缀自动机中,点数最多为\(2n-1\),边数最多为\(3n-4\)。
-
双哈希比较两个值是否相等的时候,算一个模数比较一个模数。因为算一个模数就已经发现不相等的概率还是很大的,这样就大概率可以少一次运算(因为有取模所以很慢)。
-
写多项式系列的时候,如果不注意,开的临时数组很多,注意空间(可以假装是\(O(n\lg n)\)空间)。
-
在FFT的
multi(c,a,b,n)
中,如果\(n\)表示项数为\(x^0\dots x^n\),那么在后面进行模\(x^n\)意义操作的时候,记得用\(n-1\)。这个在倍增(求逆,求exp,牛顿迭代)的时候很致命,可能让空间加倍。 -
在dinic中,BFS时如果遍历到\(T\)就直接退出,这样有时候会快很多。lyl-sap在遍历到\(T\)之前都在不断迭代加深,可以直接BFS算出遇到T之前迭代加深之后的状态(
dis,gap,cur
),再进行后面的操作。这个BFS的过程也可以用前面那个优化。 -
如果开了O2,注意一下O2对程序某些不寻常的优化(比如说某个循环对外面的变量没有影响,也没有输出,那么它可能会直接把循环跳过。在注释掉输出看看程序运行时间的时候可能要注意?)
-
写凸包如果采取极角排序的形式(见注释):
- 因为零向量(由重复点产生)是个奇怪的东西,所以应该要避免它出现。
- 因为三点共线时,用叉积只会显示\(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); }
-
在noilinux中按了ctrl+alt+f11会黑屏,出现这种情况用ctrl+F7。
-
set
中的iter.erase()
返回值(下一个位置)只有C++11支持。 -
写实数二分的时候,如果后面要计算方案,最好二分之后
check(r)
(或check(l)
)。 -
unique
中如果要加cmp
函数,注意是判断是否相等而不是比大小。 -
unorded_set
如果用一些其它的类型可能要首先哈希函数(int
和long long
不用,其它没试过)。 -
竞赛图中没有大小为\(2\)的强联通分量……
-
random_shuffle
后面可以加随机函数。写成int myrand(int i){return rand()%i;}
这样。 -
递归的时候,那些看起来在递归前已经释放,或在递归后才定义的变量,包括函数返回值(
min
,max
,不过我不知道为什么运算符没事),可能一直占空间直到退栈。看这题。另外开O2可能导致空间扩大。 -
'??-'
是组合字符(类似于\n
这种),等于'~'
。 -
自己写的spj不要写错(如果可以的话要用spj测测样例?)
-
用全文替换的时候要注意一下自己有没有替换掉不该替换的东西(比如
\n
中的n,"r"
中的r)。