目录
1. 赋初始值
2. 变量类型
3. 子程序
4. 强制转换
5.运算上溢 取模运算
6.保留字
7.位运算
8.运算符
9.数组
10.脑残bug集锦
11.变量&数组&时间
12.C++编译器
1. 赋初始值
如果是上来就需要进行运算的变量,一定要先赋初始值再用!!!
(1)布尔型变量:默认初始值指不定是啥,有一回跑程序的时候发现居然既不是true也不是false,直接把while循环卡TLE了,直接爆零;
(2)整型变量:子函数中显然会随机分配一个初始值,主程序里默认是0,然而本机上跑是这样,其他地方就不一定了,保险起见该赋成啥赋成啥,以免正式比赛爆零哭死。。。
(3)数组:必要性和(2)一样。可以用memset函数,头文件<cstring>或<memory.h>。
以下几种赋法仅限于memset使用,如果是单个变量会赋成其他值,如0x3f会赋成63!!!
① 赋0用memset(f,0,sizeof(0));
② 极大值的几种赋法:
0x3f:1061109567——10^9(int)
4557430888798830399——4*10^18(long long)
0x7f:2139062143——10^9(int) 32位最大值,当心爆炸
9187201950435737471——9*10^18(long long)
③ 极小值的赋法:
-128:-2139062144——10^9(int)
-9187201950435737472——9*10^18(long long)
④ 布尔型:memset(f,true,sizeof(f))
2. 变量类型
(1)scanf和printf里int和long long不能混用。
假如设一个long long型变量n,读入的是scanf(“%d”,&n),结果保不定会给n赋成啥值,反正不是你想要的n。
懂?当你敲完代码却无论如何都调试不过时,瞅瞅是不是读入出了问题,也许会有惊喜呦~
(2)scanf和printf里float和double和long double不能混用。
xp表示乱搞得更厉害了……反正不管怎么赋,它都不会给你赋成原本的n……说起来scanf就是个大小姐,毫无回旋余地,而C++又是个不管事儿的,编译从来不报错……少年,还是静态查错吧……
正确类型:
int | %d |
long long | %lld |
float | %f |
double | %lf |
long double | %llf |
K位小数 float | %.kf |
还有,printf也是个蛇精病,long double会玩出神奇的花样。。。
所以浮点数还是多用cin和cout,以防爆炸。。。
(3)函数中的参数类型
有的时候我们需要调用C++自带的数学函数库,那么应该怎样传入参数,跑出来的又是什么样的结果呢?
① log()
log()函数默认以e为底,也就是说实际上跑的是f(x)=ln x。然而大部分情况下我们用到的都是以2为底的log2x,那么用换底公式改一下就好log(x)/log(2)。
实际上log跑出来是啥关键跟运算结果有关,结果是整数,跑出来就是整数;结果是小数,就传出小数,精度跟传出变量类型有关。
② pow()
pow(a,b)——a的b次方。
虽然不知道咋跑的但似乎int和double都可以跑的样子。。。
3. 子程序
对于需要递归的过程,子程序是必须的,而且有时为了美观整洁,也需要把程序分块填装到子程序中在嵌到主程序里。有时不需要传入参数,但有时候还是有必要的,这时候就要注意变量类型的统一,整型和浮点数不能乱搞,int和long long、float和double最好也不要乱搞。。。中规中矩地该用啥用啥该是啥是啥最保险了。
4. 强制转换
很多时候当前已有的变量不能满足运算需要(精度啦上界啦内存啦。。。),需要进行转换。没啥好说的直接来就行。。。举例:n=(int)m,m=(double)(n)……但是有时还是要留心:m=(double)n1/(double)n2,这里分子分母都转化了才能用。
5. 运算上溢取模运算
很多题数据范围都很坑,为了防止运算上溢情况的发生,作为累加、累乘或者乘方结果的变量一般用long long比较保险。
有的题目明确指出要对p取模,那么跑程序分时候就不要犹豫,管他是加是减是乘是除还是乘方,运算一次取一次模,以防爆炸。
加:(a+b)%p=(a%p+b%p)%p
减:(a-b)%p=(a%p-b%p)%p;
乘:(a*b)%p=(a%p*b%p)%p;
除:(a/b)%p=(a%p*inv(b))%p=(a%p*pow(b,p-2)%p)%p
inv(b)表示b的逆元,b、p互质时可以用费马小定理求得inv(b)=bp-2
如果是连乘,一定要小心,根据数据范围判断一下,有时可能用交换律调整一下顺序就能避免乘积过大溢出的杯具。
6.保留字
C++ 74个保留字 不要拿来设变量。
count find merge size ends end 等等,最好不要用,可以加个下划线啥的,或者缩成cnt、siz等等,尽量避开。
7.位运算
别和cin、cout混用,C++说<<、>>傻傻分不清楚。
>> 右移一位,相当于*2
<< 左移一位,相当于/2
8.运算符
<<和>>:参考楼上
=和==:前者赋值,后者判断(什么时候调试发现变量莫名其妙自己动了的时候,找if)
&和&&:前者按位与,后者and
|和||:前者按位或,后者or
9. 数组开太小
有时候打暴力或者测样例的时候会随手开个小数组(事实证明这个习惯简直糟糕透了),然后……交题的时候就忘了改了……一片RE……
C++数组从0开,习惯从1开始存的娃儿要注意开a[100000+1],保险起见最好再开大点。数组开大不要钱嘛!
10. 脑残问题集锦(仅针对本人)
①设好n不读入:读入时不结束(循环一直进行),或者只能读入一部分数据就卡住。
②int mian:嗯,手滑,手滑。。。
③for (itn i=1;i<=n;i++):嗯,再次手滑,手滑。。。
④文件名不合法:我犹记得第一回出这种bug时我还是个pascal菜鸟,一时兴起打了个程序“-w-”还是啥来着,然后……因为这个(2,+∞)的文件名被嘲笑了半个月……不过显然比赛中要关注的还是.cpp/.in/.out的文件名。对,一定要关联文件!!!!
⑤不输入using namespacestd;:一般会在cin、cout处报错
int 2147483647 (2^31-1)
long long 2^63-1
数组(int型,开long long型要除以2)
一维数组 3*10^7(33554432)
二维数组 5000^2左右(2.5*10^7)
三维数组 300^3左右(2.7*10^7)
四维数组 75^4左右(31640625,高度危险)
时间(均为不带常数状态下跑的)
O(n) 3*10^8——0.82s
O(n^2) 10^4
O(n^3) 700——0.92s(一般正解在200~300)
O(nlogn)
2^31——2s超时
1.6*10^7(2^24)—— 1s超时
1*10^7(2^23=8*10^6)——0.64s
综上,O(n)的10^8比较稳妥,O(n^2)跑10^4没问题,O(n^3)一般就在给200~300的数据时用,O(nlogn)一般在给的数据是2^x形式或一看就是二分时用
11.编译器
这个和操作系统有关,具体不好说,看各自电脑系统是32位还是64位,一般碰到这种情况先检查一下右上角好了
还有一个至今不明的错误(如果有大神知道原因希望可以不吝赐教),一般我个人的处理方法是Ctrl+A、Ctrl+C、Ctrl+N、Ctrl+V,再重存一个文件……