LuoguB2001 入门测试题目 题解

Update

  • \(\texttt{2021.7.3}\) 经测试,本题 \(a,b\) 范围在 long long,对代码进行了修改,并修改一些笔误,更新了数据范围。
  • \(\texttt{2021.7.5}\) 应评论区要求,加上了 long (int) 的数据范围,并修改了原题中 \(a,b\) 的数据范围。
  • \(\texttt{2021.7.16}\) 去掉了数据范围中的绝对值符号,感谢 @Implicit 的提醒。另外对原文章中的一些错误进行了修改。但之后题解被无意义内容打回了,一开始决定将本题解里头涉及到的语言知识放入一个剪贴板中,并对整体编排做了较多修改,后经 WYXkk 和 XYY1411 的建议作了一些知识性方面和排版方面的修改,并重新将剪贴板的内容放在代码后面以作补充性讲解。感谢各位的建议!

Content

给定 \(a,b\),求 \(a+b\)

数据范围:\(1\leqslant a,b\leqslant4\times10^{18}\)

Solution & Code

本题解仅适用于 C++ 选手。

这道题可谓是 C++ 中最基础的题目之一,先上两份代码:

1

#include <cstdio>
using namespace std;

int main() {
	long long a, b;
	scanf("%lld%lld", &a, &b);
	printf("%lld", a + b);
	return 0;
}

2

#include <iostream>
using namespace std;

int main() {
	long long a, b;
	cin >> a >> b;
	cout << a + b;
	return 0;
}

那么这一行行语句都代表着什么呢?这两个程序如何达到相同的效果呢?接下来就对此进行详细讲解。当然,初学者如果看不懂这些东西其实也无关紧要,因为这些都将在我们今后的学习中逐渐了解。

Part 1 基础语言知识

首先我们要知道程序最开始的 #include <xx> 是什么意思,其实就是把 C++ 中的一些头文件库给包括进来,这里给几个常见的头文件库:

<cstdio> //输入/输出(这是注释)
<iostream> //输入/输出流
<string> //字符串类
<cstring> //同上
<cmath> //包含了许多实用的数学函数,例如 sin(),cos(),tan(),sqrt(),pow() 等
<algorithm> //STL 通用算法
<stack> //STL 栈容器
<queue> //STL 队列容器
<deque> //STL 双端队列容器
<map> //STL 映射容器
<vector> //STL 动态数组容器
/*
这是多行注释。
STL 可以很方便的简化我们的代码,但是常数很大,
有时运行起来很慢,所以建议知道如何手写。
这将是我们以后学习的内容,现在不知道并无大碍。
*/

对于这道基础题目,我们只需要包含 cstdio 库或者 iostream 库,因为这道题目只需要我们输出 \(a+b\) 的结果。

然后就是数据类型的问题。我们先不妨认识一些常用的数据类型。下图的这些数据类型(C++ 其他常用数据类型除外)统称为预定义类型,也就是说,这些数据类型是 C++ 已经为你准备好的。

另外,我们还将学到数组、结构体 struct 联合体 union、枚举 enum 等其他比较灵活的类型,这些数据类型统称为自定义数据类型

那我们如何选择数据类型呢?这得要从数据范围来决定。以下给出了常用数据类型在通常情况下的范围(建议去题解页面查看),对于今后做题时变量应该如何选择数据类型有很大的作用:

数据类型 范围
char \([-128,127]\)\([0,255]\),在系统字节不是 8 位的情况下可以达到更大的数据范围
unsigned char \([0,255]\),在系统字节不是 8 位的情况下可以达到更大的数据范围
signed char \([-128,127]\),在系统字节不是 8 位的情况下可以达到更大的数据范围
int/long (int) 通常情况下为 \([-2147483648,2147483647]\)(或者说 \([-2^{31},2^{31}-1]\)),但 int 只保证 \([-32768,32767]\)(或者说 \([-2^{15},2^{15}-1]\)
unsigned int \([0,4294967295]\)(或者说 \([0,2^{32}-1]\)
signed int 同 int
short int 至少 \([-32768,32767]\)(或者说 \([-2^{15},2^{15}-1]\)),但可以达到更大的数据范围
unsigned short int \([0,65535]\)(或者说 \([0,2^{16}-1]\)
signed short int 同 short int
long long (int) 通常情况下为 \([-9223372036854775808,9223372036854775807]\)(或者说 \([-2^{63},2^{63}-1]\)),但在 C++11 开始才保证至少是这个数据范围
signed long long (int) 同 long long (int)
unsigned long long (int) \([0,18446744073709551615]\)(或者说 \([0,2^{64}-1]\)
float \([1.17\times 10^{-38},3.40\times 10^{38}]\)(约数,来源于 IEEE-754 标准)
double \([2.23\times 10^{-308},1.80\times 10^{308}]\)(约数,来源于 IEEE-754 标准)
long double \([3.36\times 10^{-4932},1.19\times 10^{4932}]\)(约数,一说和 double 范围相等)

关于 long double 的一些说明:long double 是一种扩展精度浮点类型,不一定映射到 IEEE-754 所强制的类型。在 x86 和 x86-64 架构上通常为 80 位 x87 浮点类型。表格中采取的 long double 的数据范围来源于这篇文章

然后就是函数了,函数有头文件库里面自带的,也有你需要在程序中自己写出来的。比如说 sin()sort() 这些就是 C++ 的头文件库里面自带的,而通常程序中有些函数需要你自己写出来。在需要自己写出来的函数中,最重要也是最必不可少的就是 main() 函数。一般在算法竞赛中,它的类型通常是 int,而且最后通常会要求你写 return 0;,具体的意义不做赘述。除了 main() 函数和头文件库里面自带的函数外,你也可以自己写一些其他的函数,达到精简代码的效果。

然后就是输入和输出了,如果你调用的是 cstdio 库的话,它里头包含的最常用的输入函数就是 scanf()

scanf() 函数返回成功读入的数据项数,读入数据时遇到了“文件结束”则返回 EOF(End of file,即文件结尾)。例如,对于这道题,我们需要读入 \(a,b\),就用 scanf("%d%d", &a, &b),其中 %dint 这个数据类型在 scanf() 函数中的格式,其它的格式还有 %f \(\rightarrow\) float%lf \(\rightarrow\) double%lld \(\rightarrow\) long long 等。另外,scanf() 函数还有一个强大的功能,就是你可以用 scanf() 按照某一个特定的格式读入数据,例如,你想要按照 hh:mm 的个数读入 \(hh\)\(mm\),就可以用 scanf("%d:%d", &hh, &mm)。注意,如果是整形类型的话,变量前的 & 必不可少

为什么要加一个 & 号?这里不多做讲述,感兴趣的各位可以去看这个剪贴板

如果你调用的是 iostream 库的话,它里头包含的输入流(注意!并不是函数)是 cin。你就把输入流想象成一条河流,源源不断地输入数据就行,这里就不给出严格定义了。在 cin 中,每一个输入的数据以 >> 号连接,前面并不需要加 &

然后就是输出了,输出其实和输入很类似,所以这里仅作简略讲解。

scanf() 函数对应的是 printf() 函数,返回成功输出的数据项数。按照和 scanf() 一样的格式,同样也可以按照某个特定格式输出数据,例如 %8d 就可以讲一个整数按以 \(8\) 为宽度右对齐输出(详情请见 B2004),%02d 就可以将一个整数输出,并且如果不足两位的话会向前补前导零。例如 printf("%8d, %02d", 2, 2) 对应的输出结果就是:

        2, 02

另外如果题目要求保留一位小数或者你的输出应与答案的相对误差不超过 \(10^{-3}\) 等要求,那么我们就可以利用 printf 中的按位输出的格式 %.xlf或者 %.xf 输出(其中 x 里面是数字),例如保留一位小数就可以写成 printf("%.1lf", x);

输入流 cin 自然也就对应输出流 cout,你同样也可以将其想象成一条源源不断地输出数据的河流。但请注意,在 cout 中,每一个输出的数据以 << 连接!!!除了这个地方和 cin 不同之外,其他的都和 cin 并无差异。

那么 cout 是否也可以做到按位输出或者按某个特定宽度右对齐输出呢?答案是肯定的,但由于版面原因,这里并不做介绍,感兴趣的各位可以直接上网查询。

Part 2 本题你需要了解的一些东西

对于这道题目来说,\(1\leqslant a,b\leqslant4\times10^{18}\),因此我们用 long long/signed long long 数据类型。因为在算法竞赛中 long long 常用些,所以这里的代码也用的是 long long 这个数据类型。

接下来就是如何表示 \(a+b\) 了,这里就要讲到 C++ 中的数字运算符。首先就是最基本的加减乘除,它在 C++ 中分别表示为 +-*/,但请注意,/ 在 C++ 中的定义并不完全等同于我们平常的四则运算中的除法,如果两边都是整型变量,在 C++ 中,/ 默认为整除。那么有没有办法可以让它变成我们平常的除法呢?答案是肯定的,让 / 两边中的一个或者两个数变成浮点型变量(\(\texttt{float}\)\(\texttt{double}\)\(\texttt{long double}\) 等),就可以让 / 的结果变为实际结果而不是整除后的结果了。例如,\(\texttt{5/6}\) 在 C++ 中的结果是 \(0\),但如果你将其变成 \(\texttt{5.0/6}\)\(\texttt{5/6.0}\)\(\texttt{5.0/6.0}\),那么 C++ 就会输出其正确的结果 \(\texttt{0.8333\dots}\)(具体输出多少位取决于题目具体要求)。那么它的运算顺序是怎样的呢?如果仅限于加减乘除的话,它的运算顺序和平常的四则运算顺序是一样的:先乘除后加减,有括号先算括号里面的算式。只不过 C++ 里面提供运算的符号仅为小括号 () 罢了。

那么我们对于程序做了深入的剖析,想必大家都已经领会到 C++ 的语言魅力了。

Notice

本文章引用了一些网络上的内容,由于作者能力有限,可能仍不免有诸多错误,欢迎各位在评论区指出,作者将定期订正这些错误。

同时,如果您认为这篇文章侵犯了你的著作权,请及时和作者联系换下。

另外,这篇文章期间也经过了一些不小的修改,感谢 WYXkk、XYY1411、Implicit 等各路神仙对这篇文章提出的宝贵的修改建议,才使得这篇文章经过不断完善变得更加充实和严谨!

祝各位的信息之路愉快而又充实!

posted @ 2021-12-15 22:02  Eason_AC  阅读(136)  评论(0)    收藏  举报