奇迹银桥「2」
上回反响不行啊……
这会Miemeng要给大家三个考试技巧!
1.‘#’系列(预处理器)
#if #ifdef #ifndef #elif #endif #undef #define
上面这些非常有用!
我们主要说一下 #ifdef 这样的(有用!)
考试时往往要开 freopen 于是有人忘了,有人不习惯文件输入输出
所以用下面的代码片:
#include <bits/stdc++.h> using namespace std; int main(){ #ifndef LC /*LC 也可以写自己喜欢的变量名*/ freopen("*.in" ,"r",stdin); freopen("*.out","w",stdout); #endif /*照常打代码*/ }
稍稍解释一下:
#if 就类比于 if ,只是针对编译器起作用,意思就是说后面是 true 就编译下面的语句(不要以为你可以在后面加一个变量)
于是 #endif 是结束这个过程(一个 if 管全局要命啊)
那么 #ifdef 就是另一个版本,意思是说后面的宏(可以理解为一个常量)被定义了,就编译下面的语句
#ifndef 就是$if\ not\ define$,可以理解是上面的取非。
#elif 就很好理解了吧,就是类似$else \, if$的东西
如果要和 #ifdef 一起用还要写个 #elif defined A (如果上面的未定义却定义了A,就编译下面的语句,否则跳过跳到 #endif )
说这么大一坨其实也没啥用
下面说一个必须要知道的东西:
当你使用了上面的代码片后,$g++$编译,你就会发现它还是文件输入输出(不要打我啊,还没有说完)(别打脸……啊)
因为你并没有定义$LC$所以还是没法跳过 freopen ,
于是用下面的$g++$编译
g++ file.cpp -o file -D LC
后面的 -D LC 非常好理解啊,就是在编译时定义一下$LC$
发现还有一个#undef
与#define 相对
将后面的宏解除定义
#include <iostream> #define N 123 using namespace std; int main(){ cout<<N<<endl;//输出123 #undef N cout<<N<<endl;//这里会告诉你N没定义 }
剩下的就是没啥用的了:
#error #warning #pragma
#pragma大家应该都明白是什么……
这里说一个:message("sting")
用法:
#pragma message( "输出一条Note!" )
作用?也许并没有
#error是好东西(虽然没有什么用)
#error 输出一条error,不写引号也可以!
#warning就不好用了,它只能控制警告的输出QwQ(所以根本没有用)
至于到底怎么用,去$C++$手册
2.文件流 & 字符串流
别以为名字很高大上就很厉害,其实和第一课学的$cin$ $cout$是兄弟
文件流:
调库:
#include <fstream>
定义:
fstream a,b,c,d;
使用:
首先打开文件:
a.open("文件名",/*参数*/ios_base::in);
后面的参数有下面几种:
ios_base::in //输入(类比与cin) ios_base::out //输出 (类比与cout) ios_base::app//在文件的末尾cout (不新建文件)
所以给个例子:
#include <bits/stdc++.h> #define N 101010 using namespace std; void ran(){ int l,c,qn; fstream rin,rout; rin.open("std",ios_base::in); rout.open("vegetable.in",ios_base::out); rin>>l>>c>>qn; rout<<l<<" "<<c<<" "<<qn<<endl; for(int i=1;i<=l;i++){ for(int j=1;j<=c;j++){ rout<<rand()%100000<<" "; }rout<<endl; } for(int i=1;i<=qn;i++){ int a=rand()%l+1,b=rand()%c+1, c=rand()%l+1,d=rand()%c+1; rout<<min(a,c)<<" "<<min(b,d)<<" "<<max(a,c)<<" "<<max(b,d)<<endl; }rout<<endl; } int main(){ int T=N; srand(time(0)); //ran();return 0; while(T--){ if(T%10000==0)srand(time(0)); ran(); system("time ./ac"); system("./bl"); if(system("diff ac.out bl.out")){ puts("WA"); return 0; } cout<<T<<"/"<<N<<"AC"<<endl; } }
所以研究一番代码,发现这是把ran直接揉到对拍里写的
有什么好处?
- 可以把一个随机数时间种子充分利用,比如上面的代码中每个种子中,随机数用了10000次
- 可以实时控制对拍进程,发现可以再开一个流输入数据参数,于是可以在一次对拍中随时更改数据范围……
字符串流:
调库:
#include <sstream>
定义:
stringstream q;
使用:
就是把一个数输到字符串里,或是从字符串里读出一个数
(没啥用,而且特特特特特特特特特特别慢)
具体例子可以看这个一件建文件的代码(别乱改,容易爆,或者先把死循环打掉)
#include <bits/stdc++.h> using namespace std; string p,x; int main(){ fstream init; init.open("Number.pre",ios_base::in); if(init.is_open()){ init>>p>>x; } else { init.close(); init.open("Number.pre",ios_base::out); init.close(); init.open("Error",ios_base::out); init<<"Need A File Named Number.pre\nA *.pre file is for File name"<<endl; init.close(); return 0; } for(int i=0;;i++){ stringstream q; string k; q<<i; q>>k; k=p+k+x; fstream a; a.open(k.c_str(),ios_base::in); if(a.is_open()){ a.close(); } else{ fstream b; b.open(k.c_str(),ios_base::out); b.close(); return 0; } } }
3.STL的一点点骗分技巧
mt19937
基于梅森缠绕器算法,可以产生循环节长达$2^{19937}$的随机数(至于梅森缠绕器,我也不知道那是啥)
用法?和 rand() 一样
Upd:mt19937适用于值域在整个实数域的$[-Inf,+Inf]$,因为能产生均匀的正数和负数。
调库:
#include <random>
使用:
#include <bits/stdc++.h> using namespace std; mt19937 ran(time(0)); int main(){ cout<<ran()<<endl; }
好简单啊~~记得开C++11
数据结构(杂)
vector 可以 reserve 来分配内存防止它倍增到你MLE
#include <bits/stdc++.h> using namespace std; vector<int>k; int main(){ k.reserve(100); }
unordered_map 是基于 hash 的$O(1)$查询map
#include <unordered_map> using namespace std; unordered_map<int,int>k; int main(){ k.rehash(100); }
P.S. 别忘了开C++11
完结了,累死了……
可能不会再更了(倾出了毕生绝学)