【题解】【高精度】—— A+B Problem,A*B Problem,[NOIP1998 普及组] 阶乘之和
0.前言
在这篇文章中,我将会讲解三篇高精度有关的题目,以此加强对高精度的使用熟练度。这三篇题目都会用到我在高精度算法解析和高精度赛场用模板中分装的结构体,没有看过的同学可以点进去了解一下。
1.A+B Problem(高精)
题目描述
高精度加法,相当于 a+b problem,不用考虑负数。
输入格式
分两行输入。 a , b ≤ 1 0 500 a,b \leq 10^{500} a,b≤10500。
输出格式
输出只有一行,代表 a + b a+b a+b 的值。
输入输出样例
输入 #1
1
1
输出 #1
2
输入 #2
1001
9099
输出 #2
10100
提示
20 % 20\% 20% 的测试数据, 0 ≤ a , b ≤ 1 0 9 0\le a,b \le10^9 0≤a,b≤109;
40 % 40\% 40% 的测试数据, 0 ≤ a , b ≤ 1 0 18 0\le a,b \le10^{18} 0≤a,b≤1018。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 510//A和B均不超过500
struct bigint
{
int len,a[MAXN];
bigint(int x=0)//默认这个数是0
{
memset(a,0,sizeof(a));//初始化数组a
if(x==0)//如果x是0会直接跳过底下的循环,导致len被初始化为0,所以要特判
{
len=1;
return;//直接返回
}
for(len=1;x;len++)//只要x没被取完就一直去并增加这个数的长度
a[len]=x%10,x/=10;//倒序存储x
len--;//最后len总会多1
}
int &operator[](int i)//用x[i]代替x.a[i]
{
return a[i];
}
void in_data()//输入高精度整数
{
string s;cin>>s;//暂时存储到字符串里面
memset(a,0,sizeof(a));len=0;//清空数组并初始化这个大整数
for(int i=s.length()-1;i>=0;i--,len++)//字符串转整数
a[s.length()-i]=s[i]-'0';
}
void print()//打印这个数字,注意数组a是倒着存储各个数位的
{
//注意在后面的处理中可能会出现len=0的情况,所以i的初始值要写成max(num.len, 1)
for(int i=max(len,1);i>=1;i--)
printf("%d",a[i]);//前面重载过[]了,等价于num.a[i]
}
void flatten(int L)//处理1到L范围内的进位并重置长度,需要保证L不小于有效长度
{
len=L;//先赋一个初始值
for(int i=1;i<=len;i++)//处理进位
a[i+1]+=a[i]/10,a[i]%=10;
while(!a[len])//将多余的长度去掉
len--;
}
friend bigint operator+(bigint a,bigint b)//重载高精度加法
{
bigint c;//存储最终的答案
int _len=max(a.len,b.len);//累加
for(int i=1;i<=_len;i++)//循环累加
c[i]+=a[i]+b[i];
c.flatten(_len+1);//计算后最多不超过_len+1位
return c;
}
//无需重载*
};
int main()
{
bigint A,B;
A.in_data();B.in_data();//输入A和B
bigint C=A+B;//储存答案
C.print();//输出
return 0;
}
2.A*B Problem
题目背景
高精度乘法模板题。
题目描述
给出两个非负整数,求它们的乘积。
输入格式
输入共两行,每行一个非负整数。
输出格式
输出一个非负整数表示乘积。
输入输出样例
输入 #1
1
2
输出 #1
2
提示
每个非负整数不超过 1 0 2000 10^{2000} 102000。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 4010//注意分析长度
struct bigint
{
int len,a[MAXN];
bigint(int x=0)//默认这个数是0
{
memset(a,0,sizeof(a));//初始化数组a
if(x==0)//如果x是0会直接跳过底下的循环,导致len被初始化为0,所以要特判
{
len=1;
return;//直接返回
}
for(len=1;x;len++)//只要x没被取完就一直去并增加这个数的长度
a[len]=x%10,x/=10;//倒序存储x
len--;//最后len总会多1
}
int &operator[](int i)//用x[i]代替x.a[i]
{
return a[i];
}
void in_data()//输入高精度整数
{
string s;cin>>s;//暂时存储到字符串里面
memset(a,0,sizeof(a));len=0;//清空数组并初始化这个大整数
for(int i=s.length()-1;i>=0;i--,len++)//字符串转整数
a[s.length()-i]=s[i]-'0';
}
void print()//打印这个数字,注意数组a是倒着存储各个数位的
{
//注意在后面的处理中可能会出现len=0的情况,所以i的初始值要写成max(num.len, 1)
for(int i=max(len,1);i>=1;i--)
printf("%d",a[i]);//前面重载过[]了,等价于num.a[i]
}
void flatten(int L)//处理1到L范围内的进位并重置长度,需要保证L不小于有效长度
{
len=L;//先赋一个初始值
for(int i=1;i<=len;i++)//处理进位
a[i+1]+=a[i]/10,a[i]%=10;
while(!a[len])//将多余的长度去掉
len--;
}
//无需重载+
friend bigint operator*(bigint a,bigint b)//重载高精度乘法
{
bigint c;//储存答案
int lena=a.len,lenb=b.len;
for(int i=1;i<=lena;i++)//遍历a的每一位
for(int j=1;j<=lenb;j++)//遍历b的每一位
c[i+j-1]+=a[i]*b[j];//计算贡献
c.flatten(lena+lenb);//答案长度不会超过两数长度之和
return c;
}
};
int main()
{
bigint A,B;
A.in_data();B.in_data();//输入A和B
bigint C=A*B;//储存答案
C.print();//输出
return 0;
}
3.[NOIP1998 普及组] 阶乘之和
题目描述
用高精度计算出 S = 1 ! + 2 ! + 3 ! + ⋯ + n ! S = 1! + 2! + 3! + \cdots + n! S=1!+2!+3!+⋯+n!( n ≤ 50 n \le 50 n≤50)。
其中 !
表示阶乘,定义为
n
!
=
n
×
(
n
−
1
)
×
(
n
−
2
)
×
⋯
×
1
n!=n\times (n-1)\times (n-2)\times \cdots \times 1
n!=n×(n−1)×(n−2)×⋯×1。例如,
5
!
=
5
×
4
×
3
×
2
×
1
=
120
5! = 5 \times 4 \times 3 \times 2 \times 1=120
5!=5×4×3×2×1=120。
输入格式
一个正整数 n n n。
输出格式
一个正整数 S S S,表示计算结果。
样例 #1
样例输入 #1
3
样例输出 #1
9
提示
【数据范围】
对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 50 1 \le n \le 50 1≤n≤50。
【其他说明】
注,《深入浅出基础篇》中使用本题作为例题,但是其数据范围只有 n ≤ 20 n \le 20 n≤20,使用书中的代码无法通过本题。
如果希望通过本题,请继续学习第八章高精度的知识。
NOIP1998 普及组 第二题
在这里,我们定义一个fac_sum
函数,用来计算阶乘之和。
bigint fac_sum(int n)//计算阶乘之和的函数
{
bigint sum,fac(1);//用fac记录当前需要的阶乘
//循环累乘,fac*i就会直接更新当前阶乘的值,无需双重循环
for(int i=1;i<=n;i++)
{
fac=fac*i;
sum=sum+fac;//累加
}
return sum;
}
注意,如果想要初始化一个bigint类型的变量为n,你可以这样做:
bigint bignum(n);
完整代码如下
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2010
struct bigint
{
int len,a[MAXN];
bigint(int x=0)//默认这个数是0
{
memset(a,0,sizeof(a));//初始化数组a
if(x==0)//如果x是0会直接跳过底下的循环,导致len被初始化为0,所以要特判
{
len=1;
return;//直接返回
}
for(len=1;x;len++)//只要x没被取完就一直去并增加这个数的长度
a[len]=x%10,x/=10;//倒序存储x
len--;//最后len总会多1
}
int &operator[](int i)//用x[i]代替x.a[i]
{
return a[i];
}
void print()//打印这个数字,注意数组a是倒着存储各个数位的
{
//注意在后面的处理中可能会出现len=0的情况,所以i的初始值要写成max(num.len, 1)
for(int i=max(len,1);i>=1;i--)
printf("%d",a[i]);//前面重载过[]了,等价于num.a[i]
}
void flatten(int L)//处理1到L范围内的进位并重置长度,需要保证L不小于有效长度
{
len=L;//先赋一个初始值
for(int i=1;i<=len;i++)//处理进位
a[i+1]+=a[i]/10,a[i]%=10;
while(!a[len])//将多余的长度去掉
len--;
}
friend bigint operator+(bigint a,bigint b)//重载高精度加法
{
bigint c;//存储最终的答案
int _len=max(a.len,b.len);//累加
for(int i=1;i<=_len;i++)//循环累加
c[i]+=a[i]+b[i];
c.flatten(_len+1);//计算后最多不超过_len+1位
return c;
}
friend bigint operator*(bigint a,bigint b)//重载高精度乘法
{
bigint c;//储存答案
int lena=a.len,lenb=b.len;
for(int i=1;i<=lena;i++)//遍历a的每一位
for(int j=1;j<=lenb;j++)//遍历b的每一位
c[i+j-1]+=a[i]*b[j];//计算贡献
c.flatten(lena+lenb);//答案长度不会超过两数长度之和
return c;
}
};
bigint fac_sum(int n)//计算阶乘之和的函数
{
bigint sum,fac(1);//用fac记录当前需要的阶乘
//循环累乘,fac*i就会直接更新当前阶乘的值,无需双重循环
for(int i=1;i<=n;i++)
{
fac=fac*i;
sum=sum+fac;//累加
}
return sum;
}
int main()
{
int n;
cin>>n;
bigint ans=fac_sum(n);//储存答案
ans.print();//输出
return 0;
}
喜欢就订阅此专辑吧!
【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。
欢迎扫码关注蓝胖子编程教育