文章目录
前言:苦命的C++选手没有现成的高精度 ~~emo
C++高精度模板
由A <= 106 变成len(A) <= 106 (即可以存储106位数!!!)
数组下标 : 0 1 2 3 4 5 6 7 8
对应存放: 9 8 7 6 5 4 3 2 1
原因:若在数组中进位,补高位需要全部往右平移一格,而若在低位补就很方便
注意:因此主函数中逆序存放A,B,数组C的遍历从末尾开始
for(i = C.size() - 1 ; i >= 0;i-- )printf(“%d”,C[i]);
大数存储: 来自低位的进位 | 对更高位的进位(加一个位数,且若有进位肯定只能加1)
A3 A2 A1 A0
+ B2 B1 B0
_______________
------ C1 C0
新数组里面每一位存储的就是 A[i] + B[i] + t(低位的进位 0或1 )
791.高精度加法
题目描述:
给定两个正整数,计算它们的和。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的和。
数据范围
1≤整数长度≤100000
输入:
1111111111111111111111111
22222222222222
输出:
1111111111133333333333333
链接:https://www.acwing.com/blog/content/277/
更简洁一点的写法,先把大的放前面 add(大,小)
用大.size()遍历 , if(i < 小.size()) t += 小[i] ,t是每一位的数
注意点:读入是string a,b;逆序读入数组中
for(int i = a.size() - 1;i >= 0;i–) A.push_back(a[i] - ‘0’); 【记】
if(t) C.push_back(1); //如果两数相等,且有进位,t != 0,进1
C也是逆序输出:for(int i = C.size() - 1;i >= 0;i–) printf(“%d”,C[i]);
写法1:判大小:可调换保持 add(大,小)
// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)
{
if (A.size() < B.size()) return add(B, A);//先把大的放前面 add(大,小)
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i ++ ) //用大.size()遍历
{
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t); //如果两数相等,且有进位,t != 0,进1
return C;
}
推荐写法二:
简记: vector<int> add(vector<int> &A ,vector<int> &B ) 参数为 引用
for(int i = 0;i < A.size() || i < B.size();i++)
vector<int> A.或B.size() ,(size内位数值非0),
t加A,B每位上的值 ,存C.push_back(t%10) ; t /= 10;
遍历完后,最高位看是否进位: if(t)C.push_back(1); return C;
main: string a,b逆序读入到vector<int> A,B中
C逆序输出 :for(int i = C.size() - 1;i >= 0;i--) printf("%d",C[i]);
#include <iostream>
#include<cstdio>
using namespace std;
#include<vector>
//C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A ,vector<int> &B )
{
vector<int> C;
int t = 0; //来自低位的进位
for(int i = 0;i < A.size() || i < B.size();i++){ //A,B已经逆序存放,所以正序判断即从个位开始判断
if(i < A.size()) t += A[i];//判断每一位 有数就加上
if(i < B.size()) t += B[i];
C.push_back(t % 10); //t 赋值为 A[i] + B[i] + t , t % 10 存余数
t /= 10; //看t要不要进入下一位 (更高位) 大于10,t变为1 --> true
}
if(t) C.push_back(1); //进位到 更高位 赋1
return C;
}
int main(){
string a,b;
vector<int> A,B,C;
cin >> a >> b; // 举个例子 :a = "123456" -- 对应A赋值
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'); //(注意:最后一位对应数组下标是size() - 1)
C = add(A,B); //auto会自动判断类型 这里等价于vector<int> Dev_c++好像无法判断 ,把C先和A,B一起创建了
for(int i = C.size() - 1;i >= 0;i--) printf("%d",C[i]);
return 0;
}
792.高精度减法 (模板定为A,B为正整数 若A-B < 0 前面先输出"-",再逆序输出C)
C = A - B
①若A < B ,交换AB 算B - A ,前面加一个负号 ‘-’
②若A > B ,
t = A[i] - B[i] - t (t表示当前这一位的值)
若 t < 0 返回t+10 ,若t > 0 返回t -->合二为一 : (t + 10) % 10
给定两个正整数,计算它们的差,计算结果可能为负数。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的差。
数据范围
1 ≤ 整 数 长 度 ≤ 105
输入样例1:
11111111111111111111111111
11111111111102
输出样例1:
11111111111100000000000009
//判断是否有 A >= B
bool cmp(vector<int> &A,vector<int> &B) { //cmp【比较】
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];
}
}
return true; //都相等,则A == B
}
vector<int> sub(vector<int> &A ,vector<int> &B ) { //t表示当前这一位的值
vector<int> C;
for(int i = 0,t = 0; i < A.size(); i++) { //初始t表示是否借位 0/1 ,一就表示低位(不够减了)向高位借位
t = A[i] - t;
if(i < B.size()) t -= B[i]; //i代表A的位数,若剩下的高位就不用减B,因为B未赋值,剩下的直接放入C中就可以了
C.push_back((t + 10) % 10);
if(t < 0) t = 1;//判断要不要借位,小于0,向前面借一位 (即判断下一位要减一,先把t初始为1 ,减去t时等效减一)
else t = 0;
}
//去掉前导0
while(C.size() > 1 && C.back() == 0)C.pop_back();
//如 减完结果 003 ,前面的0要舍去 | C.back()就表示最高位若为0舍去 |但如果这个数是0还是要保留
return C;
}
#include<string>
int main() {
string a,b; //举例 :a = "123456";
vector<int> A,B,C;//my~先创建好ABC
cin >> a >> b;
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'); //字符串存的字符型整数 要转成真正的整数 int型
if(cmp(A,B)) { //注意:减法要先判断是否 A >= B ,若A < B 则要交换一下,且最后加个负号
C = sub(A,B);
for(int i = C.size() - 1; i >= 0; i--) printf("%d",C[i]);
} else { //相减结果小于0 , 先加个负号
C = sub(A,B);
printf("-");
for(int i = C.size() - 1; i >= 0; i--) printf("%d",C[i]);
}
return 0;
}
793.高精度乘法
题目描述:
给定两个正整数A和B,请你计算A * B的值。
输入格式
共两行,第一行包含整数A,第二行包含整数B。
输出格式
共一行,包含A * B的值。
数据范围
1≤A的长度≤100000,
1≤B≤10000
输入样例:
11111111111111111111111111111111111111111111111
9
输出样例:
99999999999999999999999999999999999999999999999
思路:
把(int)B看做一个整体和(string)A每位相乘
#include<string>
#include<iostream>
#include<vector>
using namespace std;
vector<int> mul(vector<int> &A,int b) {
vector<int> C;
int t = 0;//进位
for(int i = 0; i < A.size() || t; i++) { //【清除前导0】 C的最后一位是 乘积的第一位
if(i < A.size()) t += A[i] * b;
C.push_back(t % 10);//只取t的个位
t /= 10;//整除10后是进位
}
while (C.size() > 1 && C.back() == 0) C.pop_back();//【清除前导0】 C的最后一位是 乘积的第一位
return C;
}
int main() {
string a;
int b;//注意b为一个整体!!
cin >> a >> b;
vector<int> A,C;
for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); //a[i] 里面存的是字符型整数,要转化为真正的整数 需减去 '0' 成int型
C = mul(A,b);
for(int i = C.size() - 1; i >= 0; i--) printf("%d",C[i]);
return 0;
}
794. 高精度除法 (高精度A / 低精度B)
题目:
给定两个非负整数A,B,请你计算 A / B的商和余数。
输入格式
共两行,第一行包含整数A,第二行包含整数B。
输出格式
共两行,第一行输出所求的商,第二行输出所求余数。
数据范围
1≤A的长度≤100000,
1≤B≤10000
B 一定不为0
输入样例:
1000000000000000000000000000000000000000000000000000000
9
输出样例:(商 、余数)
111111111111111111111111111111111111111111111111111111
1
思路: 结果的每一位为余数
(余数r * 10 + 被除数的下一位A[i] ) / 除数 == 新的余数r/b存入C ,r更新为新的余数 r %= b
#include<algorithm>
vector<int> div(vector<int> &A,int b,int &r) { //参数多了 &r //正着会更好算,但是为了高精度运算统一,都逆序存
vector<int> C;
r = 0;//初始余数0
for(int i = A.size() - 1; i >= 0; i--) { //先正着算出结果
r = r * 10 + A[i]; //余数
C.push_back(r / b); //除以除数得到的下一位余数存入C
r %= b; //变成下一位余数
}
reverse(C.begin(),C.end()); //再逆序存放 #include<algorithm>
//除去前导0
while(C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main() {
string a;
int b;
cin >> a >> b;
vector<int> A,C;
for(int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); //a[i] 里面存的是字符型整数,要转化为真正的整数 需减去 '0' 成int型
int r;
C = div(A,b,r);
for(int i = C.size() - 1; i >= 0; i--) printf("%d",C[i]);
cout << endl << r << endl; //输出余数
return 0;
}
795.前缀和差分
输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。
输出格式
共 m 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
前缀和数组下标从1开始 ,公式s[i] = s[i - 1] + a[i]
前缀和数组作用:O(1)算出[l,r]区间的值的和 s[r] - s[l - 1]
下标从1开始原因 : 比如∑a[1~r],需写成 s[r] - s[0] (若从0开始,则如这样的边界求和,第一个下标要变成-1,还要加个if判断)
const int N = 100010; //如果修改数据只要在一个地方修改,而且0很多时就不会写错
int n,m;
int a[N],s[N];
int test_05(){
//ios::sync_with_stdio(false); //使得cin与标准输入输出失去同步,加快读取速度 ,副作用:不能用scanf了
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= n;i++) s[i] = s[i - 1] + a[i]; //前缀和的初始化
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",s[r] - s[l - 1]); //区间和的计算
}
return 0;
}
减少空间优化法,但不保存原数组
for(int i = 1;i <= n;i++) scanf("%d",&s[i]); //减少空间优化法,但不保存原数组
for(int i = 1;i <= n;i++) s[i] += s[i - 1]; //前缀和的初始化
796.子矩阵的和 【二维前缀和】
输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含四个整数x1, y1, x2, y2,表示一组询问。
输出格式
共q行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
-1000≤矩阵内元素的值≤1000
输入样例:
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
输出样例:
17
27
21
思路:
1.s[i,j]如何计算
2.(x1,x2),(x2,y2) 分别子矩阵为左上角和右下角坐标
3. sx2,y2 = s[x1,y1](大到边界) - s[x1 - 1,y2] - s[x2,y1 - 1] + s[x1 - 1, y1 -1]
(s[x2,y2]指x2*y2的矩阵 ,其他同理 ,思想:大矩阵 - 边界的矩阵(分3个部分) + 重复多减的部分)
画图推导dp s[i,j] = s[i-1,j] + s[i,j-1] - s[i-1,j-1] + a[i,j]
子矩阵左上角和右下角坐标 (x1,y1) ,(x2,y2)
s[x2][y2]指的是:从边界开始的 x2 * y2 的矩阵
子矩阵求和 == s[x2][y2] - s[x1][y2 - 1] - s[x2][y1 - 1] + s[x1][y1]
构造差分数组 b[i] = a[i] - a[i - 1]
【画图想象子矩阵的加 == 用大矩阵全加上再减去部分不属于子矩阵的】
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,m,q; //q次询问
int a[N][N],s[N][N];
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i = 1;i <= n;i++){ //从1开始
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++){ //(0,0) ~ (i,j) 的子矩阵和 (容斥原理,有些类似DP)
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
while(q--)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);//输入顺序依据题目要求 ! !!
printf("%d\n",s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]); //求(x1,y1) ~ (x2,y2)子矩阵的和
}
return 0;
}
797. 差分
输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l,r,c,表示一个操作。
输出格式
共一行,包含n个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
思路分析.记:
给定a[1],a[2],…a[n]构造差分数组b[n],使得
a[i]=b[1]+b[2]+…+b[i]【初始化仍用差分赋值a[i],同时也操作赋值了b[i]】
要使a[L~R]+=c,等价于 b[L]+=c, b[R+1]-=c;【边界操作】
原理:a[1~L-1]=b[1]+b[2]+…+b[L-1];a[1 ~L-1]无影响
a[L~R]=b[1]+b[2]+…+b [ L ](此时的b[ L ]加上了c)+…;所以等价于a[L ~R]中的每个数都加上了c。
a[R+1~ N]=b[1]+b[2]+…+b[ L](此时的b[ L]加上了c)+…+b[ R+1](此时的b[ R+1]减去了c,两者抵消)+…;所以a[R+1~N]无影响。
初始化:b[n]的每个元素都为0,利用差分函数构造差分数组。
使得前缀和数组a的区间增减仅需O(1)复杂度
void insert(int l,int r,int c){
b[l]+=c;//包括l且不包括r+1的区间+c ,即a[l~r]区间
b[r+1]-=c;//【包括r+1的区间且不包括l的区间不受影响】即[1,l-1] 和[r+1,最后]不受影响
}
#include <iostream>
using namespace std;
int N = 1e5 + 5;
int a[N],b[N];
int n,m;
void insert(int l,int r,int c){
b[l]+=c;//包括l且不包括r+1的区间+c ,即a[l~r]区间
b[r+1]-=c;//【包括r+1的区间且不包括l的区间不受影响】
}
int main()
{
int l,r,t;
cin>>n>>m;
for(int i=1;i<=n;i++){ //下标从1开始,差分赋值初始化a[i]和b[i]
cin>>a[i];
insert(i,i,a[i]); //初始化b[i]
}
while(m--){//m次差分操作【题目要求】
cin>>l>>r>>t;
insert(l,r,t);
}
t=0;
for(int i=1;i<=n;i++){
t+=b[i];//对差分数组求和得到区间增减后的数组
cout<<t<<" ";
}
return 0;
}
798.差分矩阵
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
-1000≤c≤1000,
-1000≤矩阵内元素的值≤1000
输入样例:
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
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
给定原矩阵a[i,j],构造差分矩阵 b[i,j],使得a[][]是b[][]的二维前缀和
差分核心操作:给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的所有数a[i,j] ,加上C 【包含这个格子的大矩阵都被影响要+C】
对于差分数组的影响:
b[x1][y1] += c;//x1,y1之前的矩阵不受影响,到了[x1,y1]才+C
b[x1][y2+1] -= c; //旁边的矩阵不受影响
b[x2+1][y1] -= c;
b[x2+1][y2+1] += c;//x2,y2之后的大矩阵不受影响 【 ±± C == 0 】
简记:
初始化赋值:读入a[i][j] ; b[i][j]用 :insert(i,j,i,j,a[i][j]); (从1开始)
**二维差分:**
b[x1][y1] += c
b[x2 + 1][y1] -= c
b[x1][y2 + 1] -= c
b[x2 + 1][y2 + 1] += c
算出新的二维前缀和数组:
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j];
#include<bits/stdc++.h>
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;//x1,y1之前的矩阵+C 仅仅到了x1,y1才+c
b[x1][y2+1] -= c; //旁边的矩阵不受影响
b[x2+1][y1] -= c;
b[x2+1][y2+1] += c;//x2,y2之后的大矩阵不受影响 +--+ C == 0
}
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;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
insert(x1,y1,x2,y2,c);
}
for(int i = 1; i <= n; i++) {//差分数组b的前缀和数组为: a
for(int j = 1; j <= m; j++) {
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j -1] + b[i][j]; //二维前缀和公式
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
printf("%d ",a[i][j]);
}
puts("");
}
return 0;
}