首页 > 编程语言 >C++ 入门防爆零教程(上册)

C++ 入门防爆零教程(上册)

时间:2023-11-06 22:00:10浏览次数:32  
标签:10 GCC texttt C++ 上册 防爆 Part pragma optimize

## C++ 入门防爆零教程(上册)
######  C++ Introductory Explosion Proof Zero Tutorial(Volume $1$)
编写者:美公鸡(洛谷账号:beautiful_chicken233,电话:$155****7747$,如有需要请随时联系)
编写时间:$2023.10.5 \sim ?$
地址:湖南省长沙市雨花区明升异城 $11$ 栋 $3902$
出版社:美公鸡出版社
价格:$0.38$ 元
### Part $0$ 目录
###### Directory
##### Part $1$ 赛时注意事项
###### $...$ Part $1.1$ 文件读写
$.....$ Part $1.1.1$ $\texttt{freopen}$
$.....$ Part $1.1.2$ $\texttt{fstream}$
$.....$ Part $1.1.3$ $\texttt{fopen}$
###### $...$ Part $1.2$ 源程序的存放
###### $...$ Part $1.3$ 恶意程序
###### $...$ Part $1.4$ 卡常
$.....$ Part $1.4.1$ 关闭同步流
$.....$ Part $1.4.2$ 关闭缓冲区自动刷新
$.....$ Part $1.4.3$ 快读快写
$.....$ Part $1.4.4$ 底层优化
##### Part $2$ 算法
###### $...$ Part $2.1$ 时间 & 空间复杂度
$.....$ Part $2.1.1$ 时间复杂度
$.....$ Part $2.1.2$ 空间复杂度
###### $...$ Part $2.2$ 枚举
###### $...$ Part $2.3$ 模拟
###### $...$ Part $2.4$ 排序
$.....$ Part $2.4.1$ 选择排序
###  Part $1$ 赛时注意事项
###### Games-time precautions
#### Part $1.1$ 文件读写
在 CSP 等重大比赛中,最重要的就是文件读写,在每一年都有许多竞赛选手因为文件读写而爆零。而文件读写都很多类,比如 `freopen` 等等。下面介绍的是 $3$ 种常用的文件读写。
##### Part $1.1.1$ $\texttt{freopen}$
在程序中加上 $\texttt{freopen("xxx.in/out", "r/w", stdin/stdout);}$ 之后,程序就会从 $\texttt{xxx.in}$ 中读取输入文件,在 $\texttt{xxx.out}$ 中输出。
##### Part $1.1.2$ $\texttt{fstream}$
首先需要在主函数外面定义 $\texttt{\#include <fstream>}$ 头文件和 $\texttt{ifstream fin("xxx.in")}$ 和 $\texttt{ifstream fout("xxx.out")}$。然后在程序中要文件读写的写成 $\texttt{fin}$ 和 $\texttt{fout}$ 即可。
##### Part $1.1.3$ $\texttt{fopen}$
首先要定义 $\texttt{FILE *file = fopen("xxx.in", "r+/w+")}$。然后在程序中使用 $\texttt{fscanf(file, "", ...)}$ 和 $\texttt{fprintf()}$ 即可。
#### Part $1.2$ 源程序的存放
在比赛中,源程序的存放是比较重要的,源程序的错误存放会导致失去分数。
一般的存放结构如下:
$\texttt{|-your name (folder)}$
$\texttt{|---T1 name (folder)}$
$\texttt{|------T1 name.cpp}$
$\texttt{|---T2 name (folder)}$
$\texttt{|------T2 name.cpp}$
$\texttt{|---T3 name (folder)}$
$\texttt{|------T3 name.cpp}$
$\texttt{|---T4 name (folder)}$
$\texttt{|------T4 name.cpp}$
#### Part $1.3$ 恶意程序
在比赛中,恶意程序的分数会变为 $0$,且会有相应的惩罚,**一定不要做**。
恶意程序的杀伤力如下表:
| 代码 | 杀伤力 | | :-----------: | :-----------: | | $\texttt{while (1) \& Sleep()}$ | 低 | | $\texttt{system("")}$ | 中 $\sim$ 高 | | $\texttt{\#include <con>}$ | 极高 |

### Part $1.4$ 卡常
##### Part $1.4.1$ 关闭同步流
C++ 的输入输出的缓冲区和 C 是同步的,我们可以用 $\texttt{ios::sync\_with\_stdio(0)}$ 关闭同步流而加快速度。
##### Part $1.4.2$ 关闭缓冲区自动刷新
程序会在输入和输出时刷新缓冲区(特别是 `endl`,想快一点就用 `'\n'`),我们可以用 $\texttt{cin.tie(0), cout.tie(0)}$ 关闭缓冲区自动刷新而加快速度。
##### Part $1.4.3$ 快读快写(不推荐,比较麻烦)
**模板:**
快读:
```cpp int read() {   int s = 0, f = 1;   char ch = getchar();   while (ch < '0' || ch > '9') {     (ch == '-') && (f = -1), ch = getchar();   }   while (ch >= '0' && ch <= '9') {     s = (s << 1 + s << 3) + ch - '0', ch = getchar();   }   return s * f; }
```
快写:
```cpp void write(int x) {   (x < 0) && (putchar('-')) && (x = -x);   if (x > 9) {     write(x / 10);   }   putchar(x % 10 + '0'); } ```
##### Part $1.4.4$ 底层优化
**循环展开:**
恰当的循环展开可以让 CPU 多线程进行并发运算,可以提升速度。但是不恰当的循环展开可能会起副作用。
展开前:
$\texttt{for (int i = 1; i <= 300; ++ i) \{}$
$\texttt{  ans += i;}$
$\texttt{\}}$
展开后:
$\texttt{for (int i = 1; i <= 300; i += 6) \{}$
$\texttt{  ans += i;}$
$\texttt{  ans += i + 1;}$
$\texttt{  ......}$
$\texttt{  ans += i + 6;}$
$\texttt{\}}$
**火车头(指令优化):**
Tips:**NOI 禁止。**
$\texttt{\#pragma GCC optimize(3)}$ $\texttt{\#pragma GCC target("avx")}$ $\texttt{\#pragma GCC optimize("Ofast")}$ $\texttt{\#pragma GCC optimize("inline")}$ $\texttt{\#pragma GCC optimize("-fgcse")}$ $\texttt{......}$
完整代码:
```cpp #pragma GCC optimize(3) #pragma GCC target("avx") #pragma GCC optimize("Ofast") #pragma GCC optimize("inline") #pragma GCC optimize("-fgcse") #pragma GCC optimize("-fgcse-lm") #pragma GCC optimize("-fipa-sra") #pragma GCC optimize("-ftree-pre") #pragma GCC optimize("-ftree-vrp") #pragma GCC optimize("-fpeephole2") #pragma GCC optimize("-ffast-math") #pragma GCC optimize("-fsched-spec") #pragma GCC optimize("unroll-loops") #pragma GCC optimize("-falign-jumps") #pragma GCC optimize("-falign-loops") #pragma GCC optimize("-falign-labels") #pragma GCC optimize("-fdevirtualize") #pragma GCC optimize("-fcaller-saves") #pragma GCC optimize("-fcrossjumping") #pragma GCC optimize("-fthread-jumps") #pragma GCC optimize("-funroll-loops") #pragma GCC optimize("-fwhole-program") #pragma GCC optimize("-freorder-blocks") #pragma GCC optimize("-fschedule-insns") #pragma GCC optimize("inline-functions") #pragma GCC optimize("-ftree-tail-merge") #pragma GCC optimize("-fschedule-insns2") #pragma GCC optimize("-fstrict-aliasing") #pragma GCC optimize("-fstrict-overflow") #pragma GCC optimize("-falign-functions") #pragma GCC optimize("-fcse-skip-blocks") #pragma GCC optimize("-fcse-follow-jumps") #pragma GCC optimize("-fsched-interblock") #pragma GCC optimize("-fpartial-inlining") #pragma GCC optimize("no-stack-protector") #pragma GCC optimize("-freorder-functions") #pragma GCC optimize("-findirect-inlining") #pragma GCC optimize("-fhoist-adjacent-loads") #pragma GCC optimize("-frerun-cse-after-loop") #pragma GCC optimize("inline-small-functions") #pragma GCC optimize("-finline-small-functions") #pragma GCC optimize("-ftree-switch-conversion") #pragma GCC optimize("-foptimize-sibling-calls") #pragma GCC optimize("-fexpensive-optimizations") #pragma GCC optimize("-funsafe-loop-optimizations") #pragma GCC optimize("inline-functions-called-once") #pragma GCC optimize("-fdelete-null-pointer-checks") #pragma GCC optimize(2) ```

###  Part $2$ 算法
######  Algorithm
#### Part $2.1$ 时间 & 空间复杂度
##### Part $2.1.1$ 时间复杂度
人们将程序运行次数的量级以及空间的量级成为时空复杂度,用大 $O$ 表示,一般会忽略常数。现代评测机大约每秒能够运行 $2 \times 10^7 \sim 10^9$ 次,但是使用了数组就比较慢了。需要**格外注意**你的时间复杂度是否都在题目限制以内。
**时间复杂度表:**
| 时间复杂度 | 少爷评测机 | 老爷评测机 | | :-----------: | :-----------: | :-----------: | | $O(\log n)$ | $2^{10^9}$ | $2^{2 \times 10^7}$ | | $O(\sqrt n)$ | $10^{18}$ | $4 \times 10^{14}$ | | $O(\log n\sqrt n)$ | $5 \times 10^{14}$ | $3 \times 10^{11}$ | | $O(n^{\frac{3}{4}})$ | $10^{12}$ | $5 \times 10^9$ | | $O(n)$ | $10^9$ | $2 \times 10^7$ | | $O(n \log \log n)$ | $4 \times 10^8$ | $5 \times 10^6$ | | $O(n \log n)$ | $4 \times 10^7$ | $10^6$ | | $O(n \sqrt n)$ | $10^6$ | $8 \times 10^4$ | | $O(n^2)$ | $3 \times 10^4$ | $5000$ | | $O(n^2 \log n)$ | $9000$ | $1500$ | | $O(n^2 \sqrt n)$ | $4000$ | $1000$ | | $O(n^3)$ | $1000$ | $300$ | | $O(n^4)$ | $150$ | $80$ | | $O(n^5)$ | $60$ | $30$ | | $O(2^n)$ | $30$ | $25$ | | $O(2^n \times n)$ | $25$ | $20$ | | $O(n!)$ | $12$ | $10$ | | $O(n! \times n)$ | $11$ | $9$ | | $O(n^n)$ | $9$ | $8$ |
**常用时间复杂度排序:**
$O(1) < O(\log n) < O(\sqrt n) < O(n) < O(n \log n) < O(n \sqrt n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)$
##### Part $2.1.2$ 空间复杂度
类似地,算法所使用的空间随输入规模变化的趋势可以用空间复杂度来衡量。
如,开一个长度为 $n$ 的数组,那么空间复杂度是 $O(n)$。开一个长度为 $n \times n$ 的二维数组,那么空间复杂度是 $O(n^2)$。开一个长度为 $n \times 3$ 的二维数组或 $3$ 个长度为 $n$ 的数组,那么空间复杂度是 $O(3n)$。开一个长度为 $n$ 的数组和一个长度为 $m$ 的数组,那么空间复杂度是 $O(n + m)$。
#### Part $2.2$ 枚举
枚举的思想是不断地猜测,从可能的集合中一一尝试,然后再判断题目的条件是否成立。但是并非所有的情况都要枚举,有时要适当的进行一些剪枝。(如枚举 $a + b = c$ 且 $b > a$ 的个数那么 $b$ 要从 $a + 1$ 开始枚举)。
**例题:**
给出 $n$ 个数 $a_1, a_2, \cdots, a_n$ 和 $x$,求有多少对 $i, j$ 满足 $a_i + a_j = x$ 且 $j > i$。
输入样例:
```cpp 6 12 4 5 7 6 3 5 6 ```
输出样例:
```cpp 2 ```
数据范围:$1 \le n \le 10^3$,$1 \le x, a_i \le 10^9$。
分析:
我们可以先枚举 $i$,从 $1$ 枚举到 $n$。每次枚举到一个 $i$ 时枚举 $j$,从 $i + 1$ 枚举到 $n$(因为 $j > i$)。每枚举到一个 $i, j$ 时判断条件 $a_i + a_j = x$,如果满足把答案 $+1$。时间复杂度 $O(n^2)$。(准确来说是 $O\Big(\dfrac{n^2 - n}{2}\Big)$)。
代码:
```cpp #include <iostream> #include <algorithm> #include <cmath> #include <cstring>
using namespace std;
using ll = long long;
const int kMaxN = 1010, kInf = (((1 << 30) - 1) << 1) + 1;
int n, x, a[kMaxN], ans = 0; // ans 是满足条件的个数
int main() { //  freopen(".in", "r", stdin); //  freopen(".out", "w", stdout);   cin >> n >> x; // 输入 n, x   for (int i = 1; i <= n; ++ i) {     cin >> a[i]; // 输入 ai   }   for (int i = 1; i <= n; ++ i) { // 枚举 i,范围 1 至 n     for (int j = i + 1; j <= n; ++ j) { // 枚举 j,范围 i + 1 至 n       (a[i] + a[j] == x) && (++ ans); // 如果 ai + aj = x,那么答案 +1(if 压缩)     }   }   cout << ans << '\n'; // 输出答案   return 0; } ```
#### Part $2.3$ 模拟
模拟就是用代码模拟出题目所要求的操作。虽然本质上比较简单,但是码量大,很难调错。所以做模拟题的时候一定要先构思好再敲代码。
**例题:**
小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 $1970$ 年 $1$ 月 $1$ 日 $00:00:00$ 到当前时刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
数据范围:给定的时间不超过 $10^{18}$。
分析:
我们直接把输入的时间 $t$ 除以 $1000$ 变成秒(毫秒和秒之间的进率为 $1000$)。然后再时间转换,天为 $t \bmod (60^2 \times 24) \div 60^2$,小时为 $t \bmod (60^2 \times 24) \bmod 60^2 \div 60$,分钟为 $t \bmod (60^2 \times 24) \bmod 60$。这里为了方便,我们定义 $d = 60^2 \times 24$,$h = 60^2$,$m = 60$。时间复杂度 $O(1)$。注意,一定要开 `long long`!
代码:
```cpp #include <iostream> #include <algorithm> #include <cmath> #include <cstring>
using namespace std;
using ll = long long;
const int kMaxN = -1, kInf = (((1 << 30) - 1) << 1) + 1; const ll d = 24 * 60 * 60, h = 60 * 60, m = 60; // 定义常量 d, h, m
int main() { //  freopen(".in", "r", stdin); //  freopen(".out", "w", stdout);   ll t;   cin >> t; // 输入 t   t /= 1000; // 把 t 除以 1000(把毫秒转换成秒)   ll day = t % d / h, hour = t % d % h / m, _min_ = t % d % m;   // 计算天,小时,分钟(注意这里 min 只能定义成 _min_)   printf("%02d:%02d:%02d", day, hour, _min_); // 输出,格式为 dd:hh:mm   return 0; } ```
#### Part $2.4$ 排序
**凉心提醒:这一章比较长。**
排序就是将一个无序的序列排成有序的算法,下面为各个排序的性质:
| 排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 | 特殊性质 | | :-----------: | :-----------: | :-----------: | :-----------: | :-----------: | | 选择排序 | $O(n^2)$ | $O(1)$ | 否 | 可以通过额外的 $O(n)$ 空间达到稳定 | | 冒泡排序 | $O(n) \sim O(n^2)$ | $O(1)$ | 是 | 有序时速度快,可以进行剪枝 | | 插入排序 | $O(n) \sim O(n^2)$ | $O(1)$ | 是 | 当 $n$ 较小时速度很快 | | 快速排序 | $O(n \log n) \sim O(n^2)$ | $O(\log n) \sim O(n)$ | 否 | 最快的排序,有序时退化到平方级 | | 归并排序 | $O(n \log n)$ | $O(n)$ | 是 | 不易被卡,很稳定 | | 希尔排序 | $O(n) \sim O(n^2)$ | $O(1)$ | 否 | 是插入排序改进而来的排序 | | 堆排序 | $O(n \log n)$ | $O(1)$ | 否 | 常数较大 | | 桶排序 | $O(\max^{n}_{i = 1} a_i)$ | $O(n + m)$ | 是 | 空间比较大 | | 基数排序 | $O(d(r + n))$ | $O(rd + n)$ | 是 | 非比较排序 | | 计数排序 | $O(n + k)$ | $O(k)$ | 是 | 非比较排序 |
##### Part $2.4.1$ 选择排序
选择排序的思想就是在无序区里找到最大值,然后与无序区的最后一个交换位置,再把无序区的最后一个变成有序区。当有序区有 $n - 1$ 个元素时,排序完成。
例:(绿色代表有序区)
初始:$[5, 7, 2, 8, 4]$
第 $1$ 次:$8$ 最大,与 $4$ 交换位置,$[5, 7, 2, 8, 4] \gets [5, 7, 2, 4, \green{8}]$。
第 $2$ 次:$7$ 最大,与 $4$ 交换位置,$[5, 7, 2, 4, \green{8}] \gets [5, 4, 2, \green{7}, \green{8}]$。
第 $3$ 次:$5$ 最大,与 $2$ 交换位置,$[5, 4, 2, \green{7}, \green{8}] \gets[2, 4, \green{5}, \green{7}, \green{8}]$。
第 $4$ 次:$4$ 最大,与 $4$ 交换位置,$[2, 4, \green{5}, \green{7}, \green{8}] \gets[2, \green{4}, \green{5}, \green{7}, \green{8}]$。
排序后:$[2, 4, 5, 7, 8]$。
代码:
```cpp #include <iostream> #include <algorithm> #include <cmath> #include <cstring>
using namespace std;
using ll = long long;
const int kMaxN = 5050, kInf = (((1 << 30) - 1) << 1) + 1;
int n, a[kMaxN]; // 定义 n 和 a 数组
int main() {   cin >> n; // 输入 n   for (int i = 1; i <= n; ++ i) {     cin >> a[i]; // 输入 ai   }   int maxa = -1e9, idx = 0; // 存最大值,idx 是下标   for (int i = n; i >= 2; -- i) {     maxa = -1e9; // 每次取最大值前要把 maxa 赋值成极小值     for (int j = 1; j <= i; ++ j) { // 枚举最大值       if (a[j] >= maxa) { // 如果大于等于最大值         maxa = a[j], idx = j; // 更新最大值       }     }     swap(a[idx], a[i]); // 交换位置   }   for (int i = 1; i <= n; ++ i) {     cout << a[i] << ' '; // 输出   }   cout << '\n';   return 0; } ```

标签:10,GCC,texttt,C++,上册,防爆,Part,pragma,optimize
From: https://www.cnblogs.com/bc2qwq/p/Cpluspluskeep0away.html

相关文章

  • 编译器Dev-C++的安装及使用
    编译器Dev-C++的安装及使用1.Dev-C++的安装下载链接:https://acm.nyist.edu.cn/file/2/Dev-Cpp_5.11_TDM-GCC_4.9.2_Setup.exe​下载​:点击此处下载安装点击安装包选择英文点击ok选择我同意无脑选下一步路径改不改都可占不了多少空间等待安装......
  • C++二维数组输出3
    题目描述输入一个整数\(N\),输出一个N行N列的二维矩阵,矩阵中的元素按列用\(1\)~\(N\)\(∗\)\(N\)蛇形填充。输入格式一个整数\red{N}\(N\)(\(N<=10\))输出格式输出N行N列的矩阵,元素之间用一个空格隔开,行末不要有多余的空格。样例输入数据3输出数据123654789......
  • C++交换a和b的值
    题目描述交换\(a\)和\(b\)的值输入格式一行,两个整数\(a\),\(b\)。输出格式一行,两个整数\(b\),\(a\),两个整数之间用空格隔开。样例输入样例51输出样例15数据范围与提示\(a\)和\(b\)保证在int范围内。\(Code\)#include<iostream>usingnamespacestd;i......
  • C++U4-03-递推1
    上节课作业部分(点击跳转)加法原理和乘法原理递推的概念 练习题1、[兔子数列]【算法分析】初始条件:第1个月有1对兔子,第2个月有1对兔子。当大于等于3个月时:第i个月兔子数=第i−1个月兔子数+第i−2个月兔子数。【参考代码】include<iostrea......
  • 二叉查找树的实现C/C++
    二叉查找树是一种关键字有序存放的二叉树。在不含重复关键字的二叉查找树中,关键字"较小"的节点一定在关键字“较大”的节点的左子树中,“较小”一般可以由内值类型的<运算符来实现,或由重载了<运算符的类类型的<运算符来实现。“较小”的概念可以根据我们的需要有不同的实现。本文实......
  • C++中如何返回数组类型数据
    错误示范:int*test01(){ intdata[3]={1,2,3}; returndata;}intmain(){ int*result=test01(); for(inti=0;i<3;i++){ cout<<result[i]<<'\t'; }}正确示范:int*test01(){// intdata[3]={1,2,3}; int*da......
  • C++逃离陨石
    题目描述在公元\(114514\)年小朱在学校里上课,突然听见学校广播播放一条骇人听闻的消息:一群陨石将袭击我市,由于陨石积过大数量多,它们无法在撞击到地面前燃烧殆尽,将会对它撞到的一切东西造成毁灭性的打击。小朱同志开始担心自己的安全问题。他一定要在被流星砸到前,到达一个......
  • C++最自信的鱼
    题目描述人比人,气死人;鱼比鱼,难死鱼。小鱼最近参加了一个“比可爱”比赛,比的是每只鱼的可爱程度。参赛的鱼被从左到右排成一排,头都朝向左边,然后每只鱼会得到一个整数数值,表示这只鱼的可爱程度,很显然整数越大,表示这只鱼越可爱,而且任意两只鱼的可爱程度可能一样。由于所有的鱼头都......
  • C++U5-04-广度优先搜索1
    广搜逻辑  广搜代码核心思路 广搜伪代码 思维导图1、[【广搜】走迷宫] 求最少需要多少步,考虑使用广搜。从起点开始进行搜索,规则只能向上下左右走动,当搜索到终点时就结束。广搜的核心思路:初始化一个队列和数组将起点入队并标记当队列不为空且没到终点的时候 取......
  • C++使用冒泡排序算法对数组进行排序
     #include<iostream>//包含iostream库usingnamespacestd;//使用标准命名空间intmain(){//主函数intarr[]={5,3,2,8,6,7,1,4};//定义并初始化数组intn=sizeof(arr)/sizeof(arr[0]);//计算数组长度//使用冒泡排序算法对数组进......