第二课学习笔记:高精度、前缀和、差分

目录

前言: 大体内容from acwing yxc。

一、高精度

1.高精度加法

2.高精度减法

3.高精度乘法

4.高精度除法

二、前缀和、差分

1.一维前缀和

2.二维前缀和

 3.一维差分

 4.二维差分 

总结:一二维前缀和和差分,注意所有数组下标从1开始、


前言: 大体内容from acwing yxc。

一、高精度

1.高精度加法

思路没什么好说的吧,t 代表进位,t%10代表进过之后剩下的,t/10代表向下一位进的数。

#include<iostream>
#include<vector>
using namespace std;

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++)
	{
		if (i < A.size()) t += A[i];
		if (i < B.size()) t += B[i];
		C.push_back(t % 10);
		t /= 10;
	}
	if (t) C.push_back(t);
	return C;
}

int main(void)
{
	string a, b;
	cin >> a >> b;
	vector<int> A, B;
	for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - 48);
	for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - 48);
	auto C = add(A, B);
	for (int i = C.size()-1;i >= 0; i--)
	{
		cout << C[i] << "";
	}
	return 0;

}

2.高精度减法

  实际上和加法差不多,不过可以注意一下t作为进位的用法。t此时只有0和1两种取值。

//这里填你的代码^^
#include<vector>
#include<iostream>
#include<cstring>
using namespace std;
bool cmp(vector<int>&A,vector<int>&B)
{
    //判断A>=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];
    }
    return true;
}
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];//可能这个位置 B里已经没有元素了
        C.push_back((t+10)%10);
        if(t<0) t=1;
        else t=0;
    }
    while(C.size()>1&&C.back()==0) C.pop_back();
    return C;
}

int main()
{
    string a,b;
    cin>>a>>b;
    vector<int> A,B;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-48); 
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-48);
    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];
    }
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3345876/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.高精度乘法

  有别于我们自己的乘法过程,这里我们将b看做是一个整体,进位方式和加法差不多。

  在for循环中采用了一个很巧妙的方式。因为判断循环的条件只有i<A。size(),所以当我们出循环的时候,如果进位t不为0,我们就需要在最后面加上这个进位。但如下方法在循环内就搞定了。

  还有一个注意点,如果我们乘一个0,那么返回的C就是一个全是0的数组,显然是无意义的。有没有别的高位数为0的情况没仔细想,但是根据这个特例,我们知道最后要对C做一个和减法差不多的操作,把在高位的0去掉。

#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++)
    {
        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;
}


int main(void)
{
    string a;
    int b;
    cin>>a>>b;
    vector<int> A;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-48);
    auto C=mul(A,b);
    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}

4.高精度除法

    模拟一下我们正常做除法的过程,首先被除数第一位除以除数,除数给商,余数进行下一步运算。在下一步中,我们需要把被除数的下一位直接拿下来,对应的数学运算就是上一步的余数r*10+A[I]。

   至此循环内的过程就明白了,观察边界,当我们处理完被除数最后一位的时候循环停止。

因此具体实现过程如下:

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
#include<algorithm>
vector<int> div(vector<int>&A,int b,int& r)//注意是用引用的方式传入r
{
    r=0; vector<int> C;
    for(int i=A.size()-1;i>=0;i--)
    {
        r=r*10+A[i];//注意一下这一步..
        C.push_back(r/b);
        r %= b;
    }
    reverse(C.begin(),C.end());//按规定,我们还是把C翻转,这个函数在algorithm库中
    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;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-48);
    int r;
    auto C=div(A,b,r);
    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
    cout<<endl<<r;
    
    
}

二、前缀和、差分

1.一维前缀和

前缀和,一串数的前n项和。特别地令这串数的下标从1开始,我们存储S[0]=0

前缀和的作用是可以方便计算出数组一段区间子序列的和。譬如a[l]+a[l+1]······a[r]=S[r]-S[l-1]。

要生成这个S数组也很简单,利用循环,令S[i]=S[i-1]+a[i]。

2.二维前缀和

实际上S[i][j]是包括a[i][j]的左上角的所有元素的和。

作用是可以求一个子矩阵的和。

计算方法有点容斥原理的思想。左下为求和方法,右上是循环时赋值的公式。

//这里填你的代码^^
#include<iostream>
using namespace std;
int n,m,q;
const int N=1005;
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++)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//注意别顾着想s那一堆忘了a
    while(q--)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        cout<<s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]<<endl;
    }
    return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3360202/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 3.一维差分

定义:可以理解为前缀和的逆运算,对于一个数组a[i],我们构造一个b[i]使得a是b的前缀和。

构造方式如图左下角。差分的作用是快速的向a数组中[l,r]里所有元素同时加上一个值。对于这个操作,我们只需要让b[l]++,b[r+1]--;(可以留意一下对差分数组里l位置做的操作,会影响根据这个差分数组求出来的原数组,l及其之后所有位置的元素)。

有了上面的基础,我们发现其实不需要去绞劲脑汁想一个差分数组是如何构造的。我们只需要将a,b数组同时全部看成0,然后向a数组中一个个赋值,这个赋值操作实际上就是向让[i,i]区间里的数加上元素值(实际上就一个数ww),所以只需要b[i]+a[i],b[i+1]-a[i]就好了。这个思想在二维差分中体现更加明显。

//这里填你的代码^^
#include<iostream>
using namespace std;
const int N =100010;
int a[N] ,b[N];
int n,m;

void insert(int l,int r,int c )
{
    b[l]+=c;
    b[r+1]-=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]);
   while(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];
   for(int i=1;i<=n;i++) cout<<b[i]<<" ";
   return 0;

}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3361185/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 4.二维差分 

  借助差分以及二维前缀和的定义,我们可以想象二维差分就是b[i][j]左上的所有元素之和等于a[i][j].

此时我们如果要对a的一个子矩阵,x1,y1,x2,y2这里面的所有元素加上某个值。方法如图。

 向构造差分数组时也就是向a数组中x1,y1,x1,y1这里面所有数加一

这里为了防止空间溢出,所以不开一个b数组作为差分数组了,直接用a存储。

//这里填你的代码^^
#include<iostream>
using namespace std;

const int N =10010;
int n,m,q;
int a[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
    a[x1][y1]+=c;
    a[x2+1][y1]-=c;
    a[x1][y2+1]-=c;
    a[x2+1][y2+1]+=c;

}

int main()
{
    cin>>n>>m>>q;
    int x;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
             x=0,cin>>x,insert(i,j,i,j,x);
    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++)
        {
            a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
            cout<<a[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;


}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3362034/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

总结:一二维前缀和和差分,注意所有数组下标从1开始、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值