首页 > 其他分享 >洛谷刷题_P1009 [NOIP1998 普及组] 阶乘之和

洛谷刷题_P1009 [NOIP1998 普及组] 阶乘之和

时间:2022-11-14 19:55:58浏览次数:83  
标签:bignum P1009 int 高精度 len -- long 阶乘 洛谷

题目

P1009 [NOIP1998 普及组] 阶乘之和

题目链接

https://www.luogu.com.cn/problem/P1009

知识点

求阶乘正常做法:

#include <stdio.h>
long long jiecheng(long long n)
{
	long long a=1;
	for(int i=1;i<=n;i++){
		a*=i;
	}
	return a;
}
int main ()
{
	int n;
	long long sum=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		sum+=jiecheng(i);
	}
	printf("%lld",sum);
	return 0;
}

本来应该是这样做就可以了,由于n的最大值是50,所以直接这么做会超出long long类型的范围,所以这道题需要用高精度的做法来做。

这里先补充一些C++的知识:结构体中的构造函数(类似于java中的构造方法)

结构体中的构造函数就是为了方便初始化变量用的,因为在结构体中变量无法直接初始化,需要写一个方法来对变量进行初始化,如果我们写的是一个普通方法,每当我们需要初始化变量的时候,就需要去调用这个方法一次,这样较为麻烦。所以我们就可以写一个构造函数,这个构造函数不需要调用(用户也无法调用),它会在创建对象(也就是结构体变量)时,自动调用。

例:

#include <stdio.h>
#include<string>
using namespace std;

struct student{
	string name;
	int age;
	student() {//通过构造函数来进行初始化
		name="jinx";
		age=10;
	}
};

int main ()
{
	student stu;
	printf("%s\n",stu.name.c_str());//printf无法直接输出string类型,需要c_str()函数转换一下
	printf("%d",stu.age);
	return 0;
}

输出就是jinx,10。

再学一下高精度的算法

在高精度算法中,大数是用一个一维数组来进行存储的,并且数组的低位用来存放数的低位,譬如一个大数为123456,此时a[0]存储的是6,a[1]存储的5,a[2]存储的是4 ... a[5]=1。

高精度的结构:

struct bignum{
    int d[1001];
	int len;
    bignum{
        memset(d,0,sizeof(d));
        len=0;
    }
}

高精度的存储:

bignum saven(char ds[]){//这个ds字符数组中存放的就是一个大数
    bignum n;
    n.len=strlen(ds);
    for(int i=0;i<n.len;i++)
    {
        n.d[i]=ds[n.len-1-i]-'0';
	}
    return n;
}

高精度的比较:

//a>b 返回1,a<b 返回-1,a=b 返回0
int compare(bignum a,bignum b)
{
    if(a.len>b.len) return 1;
    else if(a.len<b.len) return -1;
    else
    {
        for(int i=a.len-1;i>=0;i--)
        {//从最高位往最低位开始比较
            if(a.d[i]>b.d[i])	return 1;
            else if(a.d[i]<b.d[i]) return -1;
		}
    }
    return 0;
}

高精度加法:

例如:123+678=801

这个加法的思想就是:

1.先算8+3,8+3=11,取个位1为结果,并产生高位的进位1

2.再算2+7,加上来自的进位的1,此时取个位的0为结果,再产生一个进位1;

3.最后计算1+6,再加上来自进位的1,此时没有进位,结果就为8

最终就是801。

code:

bignum add(bignum a,bignum b)
{
    bignum c;
    int jinwei=0;
    for(int i=0;i<a.len || i<b.len;i++)
    {
        int temp=a.d[i]+b.d[i];
        c.d[c.len++]=temp%10;//这里的c的len初始为0,a和b的len都已经计算过
        jinwei=temp/10;
	}
    if(jinwei!=0)
    {
        c.d[c.len++]=jinwei;
    }
    return c;
}

高精度减法:

例:321-189=132

1-9,不够减,向高位借1,高位-1,原数+10,变成11,11-9=2

2-8,不够减,要先减去被低位借的1,再向高位借1,高位-1,原数+10,2-1+10-8=3

3-1,先减去被低位借的1位,3-1-1=1

bignum sub(bignum a,bignum b){
    bignum c;
    for(int i=0;i<a.len || b.len;i++)
    {
        if(a.d[i]<b.d[i])
        {
            a.d[i+1]--;
            a.d[i]+=10;
		}
        c.d[len++]=a.d[i]-b.d[i];
        while(c.len>1 && c.d[len-1]==0)
        {
            c.len--;
        }
	}
    return c;
}

高精度乘法(高精度×低精度):

假设a是高精度的数

bignum mul(bignum a,int b)
{
    bignum c;
    int jinwei=0;
    for(int i=0;i<a.len;i++)
    {
        int temp=a.d[i]*b;
        c.d[c.len++]=temp%10+jinwei;
        jinwei=temp/10;
	}
    while(jinwei!=0)
    {
        c.d[c.len++]=jinwei;
        jinwei/=10;
	}
    return c;
}

高精度除法(高精度÷低精度):

这里要注意的是除法是从被除数的最高位开始除的。

bignum div(bignum a,int b)
{
    bignum c;
    int yushu=0;
    c.len=a.len;
    for(int i=a.len-1;i>=0;i--)
    {
        int temp=a.d[i]*10+yushu;
        c.d[i]=temp/b;
        yushu=temp%b;
	}
    while(c.len>0 && c.d[len--]==0)
    {
        c.len--;
    }
    c.d[len--]=yushu;//这一步不是很明白
    return c;
}

code

#include <stdio.h>
#include<cstring>
//#include<bits/stdc++.h>
struct bignum{
	int d[1000];
	int len;
	bignum() {
		len=0;
		memset(d,0,sizeof(d));
	}
};
bignum add(bignum a,bignum b)
{
	bignum c;
	int jinwei=0;
	for(int i=0;i<a.len || i<b.len;i++)
	{
		int temp=a.d[i]+b.d[i]+jinwei;
		c.d[c.len++]=temp%10;
		jinwei=temp/10;
	}
	if(jinwei!=0)
	{
		c.d[c.len++]=jinwei;
	}
	return c;
}
bignum mul(bignum a,int b)
{
	bignum c;
	int jinwei=0;
	for(int i=0;i<a.len;i++)
	{
		int temp=a.d[i]*b+jinwei;
		c.d[c.len++]=temp%10;
		jinwei=temp/10;
	}
	while(jinwei!=0)
	{
		c.d[c.len++]=jinwei%10;
		jinwei/=10;
	}
	return c;
}
//void print(bignum a)
//{
//	for(int i=a.len-1;i>=0;i--)
//	{
//		printf("%d",a.d[i]);
//	}
//}
int main ()
{
	int n;
	scanf("%d",&n);
	bignum a,sum;
	a.d[0]=1;
	a.len=1;
	for(int i=1;i<=n;i++)
	{
		a=mul(a,i);//这里就相当于上面的a=a*i;只是由于a太大,所以得用高精度表示
		sum=add(sum,a);
	}
	for(int i=sum.len-1;i>=0;i--)
	{
		printf("%d",sum.d[i]);
	}
	//print(sum);
	return 0;
}

小结

把以前没怎么学好的高精度捡起来再学了一下,发现网上的教程基本上都能看懂了,感觉学一下算法还是蛮有意思的吧,挺能锻炼自己的思维。这道题目的话只用到了高精度×低精度的情况,可能后面还会遇到高精度×高精度的情况,或者是高精度除法,以后遇到的时候,再继续学一下吧。

参考文章

https://blog.csdn.net/qq_46702358/article/details/120225315?spm=1001.2014.3001.5501

https://blog.csdn.net/qq_46702358/article/details/120276567

标签:bignum,P1009,int,高精度,len,--,long,阶乘,洛谷
From: https://www.cnblogs.com/Jinx8823/p/16890169.html

相关文章

  • 洛谷 P1654
    设当前枚举到第\(i\)位,\(x\)为\(i\)前面期望连续\(1\)的个数。令\(a_i=x,b_i=x^2,c_i=x^3\)。\(a\)很好转移,\[a_i=(a_{i-1}+1)\timesp_i\]\(b\)的转移考虑......
  • 洛谷-2044
    洛谷-2044思路首先对与递推式\(x_n=(a*x_{n-1}+c)\)%\(mod\),发现\(c\)的存在使得不能直接使用整数快速幂求出\(x_n\),因为\(x_n\)是关于\(a,c,x0\)的\(n\)次......
  • 洛谷 P6142
    先对\(k\)进行二分,将最值问题转化成判定问题。判定一个\(k\)是否合法时,贪心去考虑,一个节点下面的若干条链在合并时,一条链肯定和另一条使它合并后恰好满足长度限制的链......
  • 洛谷-1939
    洛谷-1939思路原来我是打算按照斐波那契数列的方法,找到一个二维矩阵,因为\(a_x=a_{x-1}+a_{x-3}\),只有两个元素。但是错了,得不到正确答案。再看题解,全都是三维矩阵,......
  • 洛谷 P6147
    和这题有点类似。首先不难发现,如果当前check的值不是\(n-1\)的约数,一定无解。然后进行一遍DFS,每次用一个multiset保存子树传来的残链长度,然后贪心的配对。最后如......
  • 洛谷 P3523
    感觉和COCI2021-2022#4的T4一模一样。显然考虑二分,设当前二分出的值是\(lim\)。那么就是称一个点能覆盖另一个点当且仅当它被选中且它与那个点的距离不大于\(lim......
  • 洛谷 P1002 [NOIP2002 普及组] 过河卒
    第一个dp(动态规划)题纪念一下先尝试暴力写一个递归,由于x与y只能增加,不存在回路。#include<iostream>usingnamespacestd;inta_x,a_y,h_x,h_y,sum=0;//a_x,a_y代表目标地......
  • 洛谷P8849 『JROI-7』hibernal 二分法题解
    题目链接题目大意:交互题,给定N=2或18或64或512或1000,其中你要通过19次以内的询问在数列1-N中找到给定的未知的两个数x和y(本题解中设x<y),每次询问......
  • 洛谷-1052
    洛谷-1052思路文字版视频版Code#include<bits/stdc++.h>usingnamespacestd;#define_u_u_ios::sync_with_stdio(false),cin.tie(nullptr)#definecfint_o_o......
  • 洛谷P5309 Ynoi 2011 初始化 题解
    题面。我也想过根号分治,但是题目刷得少,数组不敢开,所以还是看题解做的。这道题目要用到根号分治的思想,可以看看这道题目和我的题解。题目要求处理一个数组a,支持如下操作......