acwing算法基础——高精度运算和前缀和差分

作者:@黑眼圈ing
机电专业 工科男
CSDN@黑眼圈ing
每天进步一点点!


备战蓝桥杯的第三天,今天学习的是常考的高精度算法和前缀和差分,我的学习方法:1.首先了解算法的主要思想,不能单单记住模板,2.多背模板,能够做到快速的写出模板,调debug,3.坚持每天做2、3道算法题,`

先赞后看,养成习惯! ! ! ^ _ ^ ❤️ ❤️ ❤️
*码字不易,大家的支持是我坚持的动力。点赞后不要忘了关注我哦!

一、高精度加法

高精度加法模板 //C = A + B
vector<int> add(vector<int>& A,vector<int>& B)
{		if(A.size()<B.size()) return add(B,A);
		vector<int> C;
		for(int i=0,t=0;i<=A.size();i++)//从低位到高位相加
		{	t+=A[i];
			if(i<B.size()) t+=B[i];
			C.push_back(t%10);
			t/=10;//进位
		}
		if(t) C.push_back(1);//处理最高位进位
}

高精度加法思想仿照我们小学的计算方法,从低位到高位依次相加,超过10就像前进一位,首先第一位t=0,C1=(A1+B1+t)%10的余数,进位t=(A1+B1+t)/10,第二位C2=(A2+B2+t)%10的余数,进位t=(A2+B2+t)/10,接下来重复找到规律
在这里插入图片描述

高精度加法完整版

#include <iostream>
#include <vector>
using namespace std;
// C =A + B
vector<int> add(vector<int>& A,vector<int>& B)
{		if(A.size()<B.size()) return add(B,A);
		vector<int> C;
		for(int i=0,t=0;i<=A.size();i++)//从低位到高位相加
		{	t+=A[i];
			if(i<B.size()) t+=B[i];
			C.push_back(t%10);
			t/=10;//进位
		}
		if(t) C.push_back(1);//处理最高位进位
		return C;
}

int main()
{	string a,b;//数值太大要用字符串存储
	vector<int> A,B;
	cin>>a>>b;//123456
	for(int i=a.size()-1;i>=0;i--)  A.push_back(a[i]-'0');//6 5 4 3 2 1
	for(int i=b.size()-1;i>=0;i--)  B.pusn_back(b[i]-'0');
	
	auto C=add(A,B);
	
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
	
	return 0;
}

问题1:为什么有for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0')这步操作呢?
因为如果我们直接将字符串存入数组里,那么数组里a[0],a[1]等开始的位置存放的是高位,如果最高位要进1的话,就要将数据往后挪一位,时间复杂度就是O(n)。
将字符串逆序存入数组中,那么数组就是从低位到高位,最高位进位直接push_back

二、高精度减法

vector<int> sub(vector<int>& A,vector<int>& B)
{	vector<int> C;
	int t=0;
	for(int i=0;i<A.size();i++)
	{	t=A[i]-t;
		if(i<B.size()) t-=B[i];
		C.push_back(t%10);
		t/=10;
	}
	while(C.size()>1&&C.back()==0) C.pop_back();//处理前导0 "00034"的情况
	return C;
}

在这里插入图片描述

//高精度减法完整版
#include <iostream>
#include <vector>
using namespace std;
// C =A - B
vector<int> sub(vector<int>& A,vector<int>& B)
{
		vector<int> C;
		for(int i=0,t=0;i<=A.size();i++)//从低位到高位相加
		{	t=A[i]-t;
			if(i<B.size()) t-=B[i];
			C.push_back(t%10);
			t/=10;//进位
		}
		while(C.size()>1&&C.back()==0)  C.pop_back();//处理前导零
		return C;
}

int main()
{	string a,b;//数值太大要用字符串存储
	vector<int> A,B;
	cin>>a>>b;//123456
	for(int i=a.size()-1;i>=0;i--)  A.push_back(a[i]-'0');//6 5 4 3 2 1
	for(int i=b.size()-1;i>=0;i--)  B.pusn_back(b[i]-'0');
	
	auto C=sub(A,B);
	
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
	
	return 0;
}

三、高精度乘法

//高精度乘法模板 高精度乘低精度
 // C = A * b
vector<int> mul(vector<int>& A,int b)
{	vector<int> C;
	int t=0;
	for(int i=0;i<A.size()||t;i++)
	{
		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();
	return C:
}

在这里插入图片描述


//高精度乘法完整版
#include <iostream>
#include <vector>
using namespace std;
// C =A * b
vector<int> sub(vector<int>& A,int b)
{
		vector<int> C;
		int t=0;
		for(int i=0;i<=A.size()||t;i++)//从低位到高位相加
		{	
			if(i<B.size()) t+=A[i]*b;
			C.push_back(t%10);
			t/=10;//进位
		}
		while(C.size()>1&&C.back()==0)  C.pop_back();//处理前导零
		return C;
}

int main()
{	string a;//数值太大要用字符串存储
	int b;
	vector<int> A;
	cin>>a>>b;//123456
	for(int i=a.size()-1;i>=0;i--)  A.push_back(a[i]-'0');//6 5 4 3 2 1
	
	auto C=mul(A,b);
	
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
	
	return 0;
}

四、高精度除法

//高精度除法模板
// C =A / b
vector<int> div(vector<int>& A,int b,int& r)
{		vector<int> C;
		r=0;
		for(int i=A.size()-1;i>=0;i--)
		{	r=10*r+A[i];
			C.push_back(r/b);
			r %= b;
		}
 		reverse(C.begin(),C.end());
 		while(C.size()>1&&C.back()==0) C.pop_back();
 		return C;
}

高精度除法需要注意的是除法从高位开始的,因为高精度加、减和乘都是从高位到低位输出的,所以高精度除法,我们要将C数组reverse !
在这里插入图片描述

//高精度除法完整版
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// C =A / b
vector<int> div(vector<int>& A,int b,int& r)
{
		vector<int> C;
		r=0;
		for(int i=A.size()-1;i>=0;i--)//从高位到低位
		{	
			r=10*r+A[i];
			C.push_back(r/b);
			r %= b;//进位
		}
		reverse(C.begin(),C.end());
		while(C.size()>1&&C.back()==0)  C.pop_back();//处理前导零
		return C;
}

int main()
{	string a;//数值太大要用字符串存储
	int b;
	vector<int> A;
	cin>>a>>b;//123456
	for(int i=a.size()-1;i>=0;i--)  A.push_back(a[i]-'0');//6 5 4 3 2 1
	
	int r;//余数
	auto C=div(A,b,r);
	
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
	
	return 0;
}

五、前缀和

1.一维前缀和

一维前缀和多用来解决数组一部分区间的和,例如:有一个数组a[10],求从a[3]~a[6]区间的和,如果用暴力求解的话,时间复杂度是O(n),所以一般我们采用前缀和来求解
一维前缀和我们需要知道的是:1.前缀和数组b[i]=a[1]+a[2]+a[3]+...+a[i] 2.求部分和l~r a[l]+...a[r]=b[r]-b[l-1]
在这里插入图片描述

#include <iostream>
using namespace std;
const int N=1e6;
int q[N],a[N];
int n,m;

int main()
{   scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&q[i]);
    //前缀和数组
    for(int i=1;i<=n;i++) a[i]=a[i-1]+q[i];
    while(m--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",a[r]-a[l-1]);
        
    }
    
    
    
    return 0;
}

2.二位前缀和

二位前缀和用来求矩阵的子矩阵子和
二位前缀和我们要知道:s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]

在这里插入图片描述
在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 1010;

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

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", &s[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];

    while (q -- )
    {
        int x1, y1, x2, 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]);
    }

    return 0;
}

六、差分

1.一维差分

一维差分:l~r区间加上c
一维差分我们要知道:差分数组q[l] += c,q[r+1] -= c

//构造差分数组
void insert(int l,int r,int c)
{	
	q[l] += c;
	q[r+1] -= c;
}
insert(l,r,c);

大家可能会有疑问为什么插入可以构造差分数组呢?
我们假设`原数组: a1, a2, a3, …, an
构造数组:b1, b2, b3, …, bn 使得 ai = b1 + b2 + b3 + … + bi(a数组就称为b数组的前缀和数组)
一种构造方式

  • b1 = a1, ----> a1 = b1
  • b2 = a2 - a1, ----> a2 = b1 + b2
  • b3 = a3 - a2, ----> a3 = b1 + b2 + b3
  • … …
  • … …
  • b(n) = a(n) - a(n-1) ----> an = b1 + b2 + b3 + … + bn
  • 原数组是a1, a2, a3, …, an, 可假设a数组初始时全是0, 立即推 ==> 差分数组b也全是0, 而给的a数组不是0,我们可以看作是进行了n次插入(insert)操作。
  • a1, insert(1,1,a[1])
  • a2, insert(2,2,a[2])
  • a3, insert(3,3,a[3])
  • …,
  • an, insert(n,n,a[n])
void insert(int l, int r, int c) {
    b[l] += c;
    b[r + 1] -= c;
}

for (int i = 1; i <= n; i++) insert(i, i, a[i]);

假设 a[] = {1, 3, 2, 1, 4}
我们把 a 数组带入代码中走一遍,看看结果(注意 b数组是全局变量,所以默认值全为0)
b1 = b1 + a1 = 0 + 1 = 1***
b2 = b2 - a1 = 0 - 1 = -1
b2 = b2 + a2 = -1 + 3 = 2
***
b3 = b3 - a2 = 0 - 3 = -3
b3 = b3 + a3 = -3 + 2 = -1***
b4 = b4 - a3 = 0 - 2 = -2
b4 = b4 + a4 = -2 + 1 = -1***
b5 = b5 - a4 = 0 - 1 = -1
b5 = b5 + a5 = -1 + 4 = 3***
b6 = b6 - a5 = 0 - 4 = -4***
请注意带***的就是构造出来的b差分数组!!!可以将b数组求前缀和 它就是a 数**组了
`在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 100010;
int n, m;
int a[N], b[N];

void insert(int l, int r, int c) {
    b[l] += c;
    b[r + 1] -= c;
}

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

    for (int i = 1; i <= n; i++) insert(i, i, a[i]);

    while (m--) {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        insert(l, r, c);
    }  

    for (int i = 1; i <= n; i++) b[i] += b[i - 1];
    for (int i = 1; i <= n; i++) printf("%d ", b[i]);

    return 0;

}

2.二位差分

在这里插入图片描述
在这里插入图片描述

#include <iostream>
using namespace std;
const int N=1010;
int q[N][N],b[N][N];
int n,m,k;
void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x2+1][y1]-=c;
    b[x1][y1]+=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
    
}
int main()
{   scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;i<=m;i++)
        
            scanf("%d",&q[i][j]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;i<=m;i++)
         insert(i,j,i,j,q[i][j]);
    }
    
    while(k--)
    {   int x1,y1,x2,y2,c;
        cin>>x1>>x2>>y1>>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]);
            
        }
        printf("\n");
    }
    
    return 0;
}

总结

欢迎各位大佬评论,如果有哪里有错误的地方,还请各位大佬指出,我会虚心接受的!!!

每天进步一点儿!!!
感谢大佬们的支持!!!感谢大佬们的支持!!!感谢大佬们的支持!!!

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑眼圈ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值