杂类知识
杂类知识
NOI Linux
终端
可以到对应文件夹里右键,在终端中打开。
也可以 Ctrl + Alt + T 打开。
vscode
Settings
编辑设置
- auto save:自动保存,选择在文件更改后保存 
afterDelay - font size:字号
 - word wrap:控制折行方式,在下面一点,选择在视区宽度处折行 
on - insert spaces:缩进是否选用 spaces。取消勾选。(可以在设置搜索 
spaces,然后就容易找到) 
终端设置 Terminal settings
可以在设置搜索 terminal。
滑到下面,把光标样式从 block 改成 line。
主题 Themes
选择 Dark High Contrast 深色高对比度。
界面字体大小
按 Ctrl + = 放大,按 Ctrl + - 缩小。
命令行 Terminal
基本编译命令:g++ test.cpp -o test -O2 -Wall -std=c++14 -DLOCAL
注:虽然 NOI Linux 本身就是 C++14,但是其默认使用
__gnu=c++14编译,会导致abs(__int128())这种 gnu 私货过编,比赛评测时则会 CE!
查 UB 和 RE 工具:-fsanitize=address,undefined
文件输入输出流:./test < in.txt > my.out
测程序运行时间:time ./test
测可执行文件静态空间:size test
运行可执行文件时测试内存峰值:\time -v test
查看程序运行详细信息(包括时间、空间):/usr/bin/time -v ./a < a.in
修改栈空间限制:ulimit -s 1024000(数字以 kb 为单位)(注意:无法在编译命令中加入,每次开新终端时需要重新修改)
比较两个文件:diff my.txt ans.txt
gdb 调试及常用命令
编译命令:g++ test.cpp -o test -Wall -std=c++14 -DLOCAL -g
注意要加上 -g,并且不要开 -O2。
打开 gdb:gdb [可执行文件名]
如果没有写参数,也可以打开 gdb 之后再写 file [可执行文件名]。
添加短点 break:b [行号/函数名]
删除断点 delete:d [断点编号1] [断点编号2](不写编号就是删除所有断点)
运行调试 run:r < in.txt > my.out(这里可以添加文件流)
到下一个断点 continue:c
输出 print:p [变量名/某个函数] 或定长 \(n\) 输出 p a[1]@n(STL 容器同样适用)
每次变量改变时都输出 display:disp [参数同 print]
查询断点、输出信息 info:i b/disp
搜索功能 find
在左侧搜索可以搜索所有打开文件。
按 Ctrl+F 快捷键可以搜索和替换单个文件。按 Esc 关闭搜索。
搜索框右侧有三个按键,分别是【开启大小写区分】【开启完整单词匹配】【启用逻辑语言】。
按 Enter 跳到下一个搜到的位置,按 Shift+Enter 跳到上一个搜到的位置。
windows 下 vscode
常用编译命令:g++ test.cpp -o test -O2 -std=c++14 -Wall "-Wl,--stack=102400000000"
编译器
用大括号给数组赋初始值就会导致编译出来的 exe 文件包含这个数组的空间。
简单来说就是 int a[N]={}; 会让可执行文件不运行的时候也占据这个数组的内存,会导致编译出来的可执行文件过大。
因此大的数组不要用大括号赋初值。
关键字
const 和 constexpr
const 有常量和只读两个含义,如果是常量请使用 constexpr 关键字,这样定义常量的常数较小。
new 分配空间 delete 释放空间
new 关键字常数太大,动态分配空间建议预先开一个足够大的数组,每次取用。
参考博客:c++中 new的使用方法,new和delete知识总结。
new <typename> 返回一个指针。
e.g
int *it=new int;//分配一个大小为 4byte 的内存,返回头指针
int *it2=new int(1);//初始化为 1
int *it3=new int[10];//分配一个长度为 10 的 int 类型的数组,使用默认构造函数初始化,返回头指针
memset(it3,0,40);//初始化数组为 0
it3[0]=1;//数组第 0 为赋值为 1
node *p=new node;//各种类型都可以,初始化和数组用法基本相同
delete it;//释放单个空间
delete it2;
delete[] it3;//释放一段空间
delete p;
template 函数模板
C++ 中的模板特性会引发代码膨胀,因为为不同数据类型生成的函数或类的代码会根据使用次数而复制多份。内联函数
inline也是类似,简单的函数被频繁内联会导致代码在多处重复,增加了文件大小。
template <typename T> void read(T &x){...}
调用时可以写成 read(x) 或 read<int>(x)。
template <typename T> T read(){...}
调用时写 int x=read<int>()。
static
表示变量为全局变量。
inline 内联函数
事实上正式比赛会开 O2,编译器会自动内联,无需加 inline 关键字,加入可能会造成负优化。
格式参照:
inline <函数类型> <函数名> (参数){
	函数语句;
}
众所周知,函数调用需要花费栈空间。
使用 inline 函数时,编译器可能会将调用函数的语句直接替换为函数内内容,从而节省栈空间。但是会增加可执行文件内存占用。
注意:
inline 关键字仅适合简单函数(指非常短小的函数)使用,不适合存在大量循环和递归的函数使用。
示例
点击查看代码
template <typename T>
inline void write(T x){
	static int st[36];
	int top=0;
	if(x<0) putchar('-'),x=-x;
	do{
		st[top++]=x%10,x/=10;
	}while(x);
	while(top) putchar(st[--top]+'0');
}
结构体
构造函数及资源清理函数
struct node{
	int x,y;
	node ():x(0),y(0){/*里面也可以写初始化代码*/}//默认构造函数,无参数
	//node(){x=0,y=0;}//与上面等价
	node (int _x,int _y):x(_x),y(_y){/*So can it.*/}//构造函数,有参数
	//node(int _x,int _y){x=_x,y=_y;}//与上面等价
	node (int _x=0,int _y=0):x(_x),y(_y){/*So can it.*/}//这个构造函数拥有上面两种的功能
	~node(){pf("%d %d\n",x,y);}//释放内存时会自动执行
};
int main(){
	//(不止)三种初始化方式 
	node a;//a={0,0} 
	node b(1,2);//b={1,2}
	node c{2,3};//c={2,3}
	node *p=new node(1,0);
	delete p;
}
重载运算符
在类内重载:
struct node{
	int x,y;
	bool operator < (const node& b)const{return y < b.y;}
};
在类外重载:
struct node{
	int x,y;
};
bool operator < (const node& a,const node& b) {return a.y<b.y;}
bool operator > (const node& a,const node& b) {return b<a;}
bool operator <= (const node& a,const node& b) {return !(b<a);}
bool operator >= (const node& a,const node& b) {return !(a<b);}
bool operator == (const node& a,const node& b) {return !(a<b)&&!(b<a);}
更多重载运算符见这篇博客。
最常用的应该是重载模拟整型的运算符了,重载指针类以及特殊的 new,delete应该是比较少用到的。
模拟整型运算符只有 node 和 bool 两种,如果在类内重载,需要省略一个参数,只有一个参数的运算符括号内留空(特别地,在参数后面的 ++ 或 -- 括号内需要些一个 int),有两个参数的括号内写第二个参数,定义为常量 const 以及引用 &,即 const node &b(特别地,位运算左移 << 和右移 >> 参数是 unsigned n)。写完参数,括号后需要写 const。
在类外重载,不可以省略参数,括号后不需要写 const,其他一样。
还可以重载不同类型的混合运算,例如 int operator + (const node &a,const int &b) {return {a.a+b};}
STL
STL 函数
- 
reverse:翻转数组、字符串。reverse(v.begin(), v.end()) 或 reverse(a + begin, a + end)。 - 
unique:去除容器中相邻的重复元素。unique(ForwardIterator first, ForwardIterator last),返回值为指向去重后容器结尾的迭代器,原容器大小不变。与 sort 结合使用可以实现完整容器去重。 - 
lower_bound:在一个有序序列中进行二分查找,返回指向第一个 大于等于 x 的元素的位置的迭代器。如果不存在这样的元素,则返回尾迭代器。lower_bound(v.begin(),v.end(),x)。实质上是返回第一个cmp(i,x)=false的元素。 - 
upper_bound:在一个有序序列中进行二分查找,返回指向第一个 大于 x 的元素的位置的迭代器。如果不存在这样的元素,则返回尾迭代器。upper_bound(v.begin(),v.end(),x)。实质上是返回第一个cmp(x,i)=true的元素。 - 
next_permutation:将当前排列更改为 全排列中的下一个排列。如果当前排列已经是 全排列中的最后一个排列(元素完全从大到小排列),函数返回 false 并将排列更改为 全排列中的第一个排列(元素完全从小到大排列);否则,函数返回 true。next_permutation(v.begin(), v.end())或next_permutation(v + begin, v + end)。 - 
prev_permutation:将当前排列更改为 全排列中的上一个排列。用法同next_permutation。 
set 常数巨大,建议使用 priority_queue,用两个优先队列的对顶堆都比 set 好得多。
bitset
bitset<1000> bs;
bs.reset() 清零。
不知道什么库
__lg() 常数比 log2() 小。
__builtin_popcount() 计算二进制 \(1\) 的个数。可看做 \(O(1)\)。
pb_ds 库
如何优雅地使用 pb_ds 库介绍了一种可以不用背头文件的方法。
常数优化
常数优化经验是写不完的,因此不写。应当在写代码的时候就注意卡常,养成良好马蜂。
输入输出
输出缓冲区
- 无缓冲:即每次输出都马上输出。
 - 行缓冲:遇到换行才输出整行。
 - 全缓冲:程序结束才输出全部。
 
可以手动修改缓冲区。我不会。
可以使用 fflush() 手动刷新缓冲区来马上输出。
Windows 系统下默认直接输出是无缓冲输出,文件读写时全缓冲输出。
Linux 系统下默认直接输出是行缓冲输出,文件读写时全缓冲输出。
输入输出优化
1. 关闭同步流
注:写了以下代码后不可再用 scanf 和 printf,使用 cin 和 cout 效率甚至更快。
ios::sync_with_stdio(false);
cin.tie(0); 
cout.tie(0);
2. 快读快写
如果把 getchar()、putchar() 换成 getchar_unlocked()、putchar_unlocked() 可以更快,据说基本上和 fread 一样快了。但是只在 Linux 下可以过编,而且一般不需要。
一坨快读快写
#define isdigit(x) (x>='0'&&x<='9')
template <typename T>
void read(T &sum) {
	sum=0;
	T fl=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) (ch=='-')&&(fl=-1);
	for(;isdigit(ch);ch=getchar()) sum=(sum<<3)+(sum<<1)+(ch-'0');
	sum=sum*fl;
}
template <typename T,typename... args>
void read(T &x,args&... a) {
	read(x);
	read(a...);
}
template <typename T>
void write(T x) {
	static int st[35];
	int top=0; 
	if(x<0) putchar('-'),x=-x;
	do {
		st[top++]=x%10,x/=10;
	}while(x);
	while(top) putchar(st[--top]+'0');
}
template <typename T>
void write(T x,char ch) {
	write(x);
	putchar(ch);
}
template <typename T,typename... args>
void write(T x,args... a,char ch) {
	write(x,ch);
	write(a...,ch);
}
当然实际上考场上面你不会打这么长,打一个基本的快读快写就足够了。
3. C 风格输入输出
一般来讲,scanf 和 printf 足矣。
关键字的使用
可以使用 inline 加速。
register 已弃用。
实际上开了 O2 的编译器会自动内联,因此其实不需要写 inline。
循环
循环展开比较玄学,这里不讲。
可以写在一个循环里的就不要写成两个循环。
初始化
memset 比 for 快很多。
对拍详解
需要调试的程序
#include<bits/stdc++.h>
#define ll long long
#define pf printf
#define sf scanf
using namespace std;
const int N=1e5+7;
int main(){
	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	int a,b;
	cin>>a>>b;
	pf("%d\n",a+b); 
}
暴力程序/样例
#include<bits/stdc++.h>
#define ll long long
#define pf printf
#define sf scanf
using namespace std;
const int N=1e5+7;
int main(){
	freopen("in.txt","r",stdin);
	freopen("std.out","w",stdout);
	int a,b;
	cin>>a>>b;
	//暴力代码 
	int ans=a+b;
	pf("%d\n",ans+1);
}
数据生成器
#include<bits/stdc++.h>
#define ll long long
#define pf printf
#define sf scanf
using namespace std;
const int N=1e5+7;
mt19937 rd(random_device{}());
int main(){
	freopen("in.txt","w",stdout);
	int a=rd()%100+1,b=rd()%100+1;
	pf("%d %d\n",a,b);
}
对拍程序(windows 系统)
#include<bits/stdc++.h>
#include<windows.h>
#define ll long long
#define pf printf
#define sf scanf
using namespace std;
const int N=1e5+7;
int main(){
	while(1){
		system("shuju.exe");
		system("baoli.exe");
		system("test.exe");
		if(system("fc my.out std.out")){//比较是否相等,如不等,返回1
			break;
		}
	}
}
对拍程序(Linux 系统)
#include<bits/stdc++.h>
#define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
const int N=1e5+7;
int main() {
	while(1) {
		system("./shuju");
		system("./test");
		system("./std");
		if(system("diff my.out std.out")) {
			puts("WA"); break;
		}else puts("AC");
	}
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/18341956

                
            
        
浙公网安备 33010602011771号