蓝桥杯_基础算法_2 (C++高精度,前缀和,差分)

前言:苦命的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;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值