基础算法(二)

大整数在c++里是如何储存的?

把大整数的每一位存到数组里
例如有数据123456789,假设我们有一个数组a[ ],
0 1 2 3 4 5 6 7 8 一共8个空间,我们一般用第0位存个位,用最后一位存最高位,也就是
9 8 7 6 5 4 3 2 1 这是因为两个整数做乘除的时候可能会进一个数,此时高位需要补一位,在数组中的反应就是在数组的末尾补上一位,比较好加。

高精度加法

给定两个正整数(不含前导 $0$),计算它们的和。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的和。

数据范围

$1\leq整数长度\leq100000$

输入样例:

1
2
12
23

输出样例:

1
35
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int>& A, vector<int>& B)//这边使用&(引用)是为了提高效率,如果不加的话会把整个数组拷贝进来
{
vector<int> C;//定义答案c

int t = 0;//用t记录进位
for (int i = 0; i < A.size() || i < B.size(); i++)
{
if (i < A.size()) t += A[i];
if (i < B.size()) t += B[i];//完成t=A[i]+B[i]
C.push_back(t % 10);//取余
t /= 10;//进位,t大于10就会变成1,即进一
}

if (t) C.push_back(1);//在数组的最后添加一位,数据为1
return C;
}

int main()
{
string a, b;//因为太长了,所以用字符串读进来
vector<int>A, B;//把a,b存到这里

cin >> a >> b;//a="123456"
for (int i = a.size() - 1; i >= 0; i--)A.push_back(a[i] - '0');//A=[6,5,4,3,2,1],因为要逆着存,所以逆着遍历
for (int i = b.size() - 1; i >= 0; i--)B.push_back(b[i] - '0');//b[i]输出的是字符(ascII码值)
//再减去0的ascII值就能转变成正常的数字输出
auto C = add(A, B);

for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//按照我们读写习惯,应该先输出最高位,而最高位存在数组的的最后一位

return 0;
}

高精度减法

给定两个正整数(不含前导 $0$),计算它们的差,计算结果可能为负数。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的差。

数据范围

$1 \leq 整数长度 \leq 10^5$

输入样例:

1
2
32
11

输出样例:

1
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <vector>

using namespace std;

//判断是否有A>=B
bool cmp(vector<int> &A, vector<int> &B)
{
if (A.size() != B.size()) return A.size() > B.size();//判断位数
for (int i = A.size() - 1; i >= 0; i--)//位数不一样,再从高位开始逐一比较
if (A[i] != B[i])
return A[i] > B[i];//找到第一位不相等的数,A的数比较大就A大,反之也是一样
return true;//如果A=B,也返回真值
}

vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C;//定义答案c
for (int i = 0, t = 0; i < A.size(); i++)
{ //定义t为进位
t = A[i] - t;//实际上是t = A[i] - B[i] - t
if (i < B.size()) t -= B[i];//但是B需要做要做判断,判断是否有这一位数
C.push_back((t + 10) % 10);//如果t>=0,则返回t本身,如果t<0,则返回t+10
if (t < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
//例如123-120=003,这里的pop_back()能从最后数组一位删除数据
//如果答案C只有一位,且答案为0,则不能删去0
return C;
}


int main()
{
string a, b;//因为太长了,所以用字符串读进来
vector<int>A, B;//把a,b存到这里

cin >> a >> b;//a="123456"
for (int i = a.size() - 1; i >= 0; i--)A.push_back(a[i] - '0');//A=[6,5,4,3,2,1],因为要逆着存,所以逆着遍历
for (int i = b.size() - 1; i >= 0; i--)B.push_back(b[i] - '0');//b[i]输出的是字符(ascII码值)
//再减去0的ascII值就能转变成正常的数字输出
if (cmp(A, B))
{
auto C = sub(A, B);

for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//按照我们读写习惯,应该先输出最高位,而最高位存在数组的的最后一位

}
else
{
auto C = sub(B,A);

cout << "-";

for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//按照我们读写习惯,应该先输出最高位,而最高位存在数组的的最后一位
}

return 0;
}

高精度乘法

给定两个非负整数(不含前导 $0$) $A$ 和 $B$,请你计算 $A \times B$ 的值。

输入格式

共两行,第一行包含整数 $A$,第二行包含整数 $B$。

输出格式

共一行,包含 $A \times B$ 的值。

数据范围

$1 \leq A的长度 \leq 100000,$
$0 \leq B \leq 10000$

输入样例:

1
2
2
3

输出样例:

1
6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <vector>

using namespace std;

vector<int> mul(vector<int> &A, int b)
{
vector<int> C;//定义答案C
int t=0;//进位
for (int i = 0; i < A.size() || t; i++)//A没处理完或者进位没处理完
{
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;//进位
}
while (C.size() > 1 && C.back() == 0) C.pop_back();//处理前导0
//例如123-120=003,这里的pop_back()能从最后数组一位删除数据
//如果答案C只有一位,且答案为0,则不能删去0
return C;
}

int main()
{

string a;
int b;

cin >> a >> b;

vector<int> A;

for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

auto C = mul(A, b);

for (int i = C.size() - 1; i >= 0; i--) cout << C[i];

return 0;

}

高精度除法

给定两个非负整数(不含前导 $0$)$ A$,$B $ 请你计算 $A/B$ 的商和余数。

输入格式

共两行,第一行包含整数 $A$,第二行包含整数 $B$。

输出格式

共两行,第一行输出所求的商,第二行输出所求余数。

数据范围

$1 \leq A的长度\leq 100000$
$1\leq B \leq 10000$
$B$ 一定不为 $0$

输入样例:

1
2
7
2

输出样例:

1
2
3
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
vector<int> div(vector<int> &A, int b,int &r)//r是余数,b是除数
{
vector<int> C;
r = 0;
for (int i = A.size() - 1; i >= 0; i--)
//除法是从最高位开始计算,这和其他的加减乘都不同
//因此我们先输出最高位,最高位在数组的最后一位
{
r = r * 10 + A[i];
//例如113/2,先做的是1/2,不够,因此11除以2
//11除以2等于5,余数1,商写到数组的最后一位
//余数1则*10,和后面的3加到一起,做13除以2
C.push_back(r / b);
//答案商要的数和加减乘刚好相反
r %= b;
}
//此时C0位子上存的是最高位的数,这和我们开头讲的存储方式相反
reverse(C.begin(), C.end());//这里需要用到#include <algorithm>
//因此用reverse将整个数据调换位子
while (C.size() > 1 && C.back() == 0) C.pop_back();//处理前导0
return C;
}




int main()
{

string a;
int b;

cin >> a >> b;

vector<int> A;
int r;//余数

for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

auto C = div(A, b, r);

for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//输出商

cout <<endl<< r << endl;//输出余数
return 0;

}

前缀和

输入一个长度为 $n$ 的整数序列。

接下来再输入 $m$ 个询问,每个询问输入一对 $l,r$。

对于每个询问,输出原序列中从第 $l$ 个数到第 $r$ 个数的和。

输入格式

第一行包含两个整数 $n$ 和 $m$。

第二行包含 $n$ 个整数,表示整数数列。

接下来 mm 行,每行包含两个整数 $l$ 和 $r$,表示一个询问的区间范围。

输出格式

共 $m$ 行,每行输出一个询问的结果。

数据范围

$1 \leq l \leq r\leq n$
$1\leq n,m \leq 100000$
$−1000\leq 数列中元素的值 \leq 1000$

输入样例:

1
2
3
4
5
5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

1
2
3
3
6
10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;

const int N = 100010;

int n, m;
int a[N], S[N];

int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
S[i] = S[i - 1] + a[i];//前缀和的初始化
}
while (m--)
{
int l, r;
cin >> l >> r;
cout <<S[r] - S[l - 1] << endl;//区间和的计算
}
return 0;
}

二维前缀和

输入一个 $n$ 行 $m$ 列的整数矩阵,再输入 $q$ 个询问,每个询问包含四个整数 $x_{1}$,$y_{1}$,$x_{2}$,$y_{2}$,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

输入格式

第一行包含三个整数 $n$,$m$,$q$。

接下来 $n$ 行,每行包含 $m$ 个整数,表示整数矩阵。

接下来 $q$ 行,每行包含四个整数$x_{1}$,$y_{1}$,$x_{2}$,$y_{2}$,表示一组询问。

输出格式

共 $q$ 行,每行输出一个询问的结果。

数据范围

$1 \leq n,m \leq 1000$,
$1 \leq q \leq 200000$,
$1\leq x_{1} \leq x_{2} \leq n$,
$1 \leq y_{1} \leq y_{2} \leq m$,
$−1000 \leq 矩阵内元素的值 \leq 1000$

输入样例:

1
2
3
4
5
6
7
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出样例:

1
2
3
17
27
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int s[N][N], a[N][N];

int main()
{

cin >> n >> m >> q;

for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];

for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];//求前缀和

while (q--)
{
int x1, y1, x2, y2;

cin >> x1 >> y1 >> x2 >> y2;
cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
//求部分和(算子矩阵和)
}

return 0;
}


差分

存在数组$a_{1}$,$a_{2}$,$a_{3}$…和数组$b_{1}$,$b_{2}$,$b_{3}$…,使得$a_{n}$=$b_{1}$+$b_{2}$+…+$b_{n}$. a数组是b数组的前缀和。b数组是a数组的差分

一维数组的构造方式

$b_{1}$=$a_{1}$

$b_{2}$=$a_{2}$-$a_{1}$

$b_{3}$=$a_{3}$-$a_{2}$

$b_{n}$=$a_{n}$-$a_{n-1}$;

输入一个长度为 $n$ 的整数序列。

接下来输入 $m$ 个操作,每个操作包含三个整数 $l$,$r$,$c$,表示将序列中 [$l$,$r$] 之间的每个数加上 $c$。

请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数 $n$ 和 $m$。

第二行包含 $n$ 个整数,表示整数序列。

接下来 $m$ 行,每行包含三个整数 $l$,$r$,$c$,表示一个操作。

输出格式

共一行,包含 $n$ 个整数,表示最终序列。

数据范围

$1 \leq n,m \leq 100000$,
$1 \leq l \leq r \leq n$,
$−1000 \leq c \leq 1000$,
$−1000 \leq 整数序列中元素的值 \leq 1000$

输入样例:

1
2
3
4
5
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例:

1
3 4 5 3 4 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

using namespace std;

const int N = 100010;

int n, m;//n个整数 m行
int a[N], b[N];

void insert(int l, int r, int c)//范围是[l.r],在里面+c
{
b[l] += c;
b[r + 1] -= c;
}
//让数组b在b[l]处+c,在b[r+1]处—c,从而保证在[l,r]内,每个a都能+c

int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
//读入数据

for (int i = 1; i <= n; i++) insert(i, i, a[i]);
//假设初始的时候a全是0,通过插入函数让它分别等于a[1]...a[i]
//在[i,i]内,做a[i]+=c,a[i+1]-=c
//也就是让a数组的值变成a[1]...a[i]

while (m--)//m个操作
{
int l, r, c;

cin >> l >> r >> c;
insert(l, r, c);
}

for (int i = 1; i <= n; i++) b[i] += b[i - 1];//把b数组变成自己的前缀和

for (int i = 1; i <= n; i++) cout << b[i] << ' ';

return 0;
}


差分矩阵

输入一个$n$行$m$列的整数矩阵,再输入$q$个操作,每个操作包含五个整数 $x_1,y_1,x_2,y_2,c$,其中$(x1,y1)$和$(x2,y2)$表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上$c$。

请你将进行完所有操作后的矩阵输出。

输入格式

第一行包含整数$n,m,q$。

接下来$n$行,每行包含$m$个整数,表示整数矩阵。

接下来$q$行,每行包含$5$个整数$x1,y1,x2,y2,c$,表示一个操作。

输出格式

共$n$行,每行$m$个整数,表示所有操作进行完毕后的最终矩阵。

数据范围

$1 \leq n,m \leq 1000$,
$1 \leq q \leq 100000$,
$1 \leq x1 \leq x2 \leq n$,
$1 \leq y1 \leq y2 \leq m$,
$−1000 \leq c \leq 1000$,
$−1000 \leq 矩阵内元素的值 \leq 1000$

输入样例:
1
2
3
4
5
6
7
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
1
2
3
2 3 4 1
4 3 4 1
2 2 2 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c;
b[x2 + 1][y1] -= c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y2 + 1] += c;
}

int main()
{
scanf("%d%d%d", &n, &m, &q);

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &a[i][j]);

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
insert(i, j, i, j, a[i][j]);// 构造差分矩阵

while (q -- )
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1, y1, x2, y2, c);
}

for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];// 将差分矩阵改为原矩阵

for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ ) printf("%d ", b[i][j]);
puts("");// puts()函数用来向标准输出设备屏幕输出字符串并换行。此处功能等同于cout << endl;
}

return 0;
}