2023.9.6

#A. 求约数个数之一

  • 传统题1000ms256MiB

Description

输入一个数字,输出其约数个数。例如10就有4个约数1,2,5,10

Format

Input

一个数字N,N< =1000

Output

如题

Samples

输入数据 1

10

输出数据 1

4

Limitation

1s, 1024KiB for each test case.

数据范围很小,枚举2-n即可。

/*
	version 1.0
*/

#include<bits/stdc++.h>
using namespace std;
int main()
{
long long n;
int sum=0;
cin>>n;
for (int i=1;i<=n;i++)
     if (n%i==0)
           sum++;

cout<<sum<<endl;
return 0;
}

时间复杂度O(N) 

#B. 求约数个数之二

  • 传统题1000ms256MiB

同上一题,只是输入数字N小于等于2147483647

n>10^8,A题做法会超时。

容易想到因子是成对出现的:若m为n的因数,那么n/m也是

如对于12:1是12的因数,那么12/1=12也是;2是12的因数,那么12/2=6也是;3是12的因数,那么12/4=12也是。

值得注意的是,对于完全平方数如100,因数10只算一遍,需要特判

/*
	version 2.0 
*/

#include<bits/stdc++.h>
using namespace std;
int n,ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=sqrt(n);i++)
		if(n%i==0)
			if(i*i==n)
			{
				ans++;
				break;
			}
			else
				ans+=2;
	printf("%d",ans);
	return 0;			
}

 时间复杂度O(根号N)

#C. 求约数个数之三

  • 传统题1000ms256MiB

Description

输入一个数字,输出其约数个数。例如10就有4个约数1,2,5,10

Format

Input

一个数字N,N< =2^63-1

Output

如题

Samples

输入数据 1

10

输出数据 1

4

N越来越大了,不能再用枚举。

原理:一个数及其因子都可以由其质因子相乘得到。

例如;12=2^2*3^1,12的质因子为2,3;因子为1,2,3,4,6,12。

则有

1=2^0 * 3^0

2=2^1 * 3^0

3=2^0 * 3^1

4=2^2 * 3^0

6=2^1 * 3^1

12=2^2 * 3^1

乘法原理,12的因子数为(2+1)*(1+1)=6。

也就是:

把一个数N写成:N=(p1^x1)(p^x2)(p3^x3)…(pi^xi)(pi为质数),则N的因数个数为(x1+1)(x2+1)(x3+1)…(xk+1)。

/*
	version 3.0
*/

#include<bits/stdc++.h>
using namespace std;
long long n,ans=1,k;
int main()
{
	scanf("%lld",&n);
	for(int i=2;i<=sqrt(n);i++)
	{
		k=0;
		while(n%i==0)
		{
			k++;
			n/=i;
		}
		ans*=k+1;
		if(n==1)
			break;
	}
	if(n!=1)
		ans*=2;
	printf("%d",ans);
	return 0;
}

可以反证,若n%i=0,则i为质数。

若i为合数,那在之前的循环中,i作为其因子的若干倍,已经被筛掉。 

#D. 求约数个数之四之求N!的因子数

  • 传统题1000ms256MiB

Description

求N!的因子数%10^9+7

1≤N≤1000000

Format

Input

一行给出数字N

N<=1000000

Output

如题

Samples

输入数据 1

3

输出数据 1

4

输入数据 2

100

输出数据 2

583951250

13!超过int的范围,20!超过unsigned long long的范围

所以n!肯定不能直接算出来

n!=1*2*……*(n-1)*n,我们可以退而处理1-n

沿用C题的思路,运用加法原理

例如6!=1*2*3*4*5*6。

2=2^1

3=3^1

4=2^2

5=5^1

6=2^1*3^1

所以6!=2^4*3^2*5^1

所以6!的因子数为(4+1)(2+1)(1+1)=30

/*
	version 4.0
*/

#include<bits/stdc++.h>
using namespace std;
int n,prime[1000002],tot,num[1000002],MOD=1000000007;
long long ans=1,k;
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++)	
		if(num[i]==0)
		{
			tot++;
			prime[tot]=i;//筛出n以内的质数 
			for(int j=2;j<=n/i;j++)
				num[j*i]=1;	
		}
	for(int i=1;i<=n;i++)
		num[i]=i;	
	for(int i=1;i<=tot;i++)
	{
		k=0;
		for(int j=1;j<=n;j++)
			while(num[j]%prime[i]==0)
			{
				k++;
				num[j]/=prime[i];
			}	
		k++;		
		ans=ans*k%MOD;
	}	
	for(int i=1;i<=n;i++)
		if(num[i]!=1)
			ans=ans*2%MOD; 
	printf("%lld",ans);
	return 0;
}

 TLE,还是不够快

如何优化?

我们发现上面这个程序是对2-n做质因子分解,看是否能分出质因子出来。

如果变换一个思路,我们能否成批成批的找出那些一定包含5这个质因子的数字呢?

我们将这些数字进行分类考虑

只能分解出来1个5出来的,则它们可统一写成5*x这样的形式

于是当N = 1000时:

5*x<=1000,x<=200

即找出来的数字形如5,10,15,20,25,30,35,40,45,50这样形式

能分解出来2个5出来的,则它们可统一写成25 * x这样的形式

于是25*x<=1000,x<=40

即找出来的数字形如25,50,75,100这样形式,发现这些数字在前面已找出过一次了。因为一个数字能写成25 * x这样的形式,也必然可写成5 * x这样的形式。于是这些数字虽然能提供2个5出来,但在本次计算中只能累加一次。

能分解出来3个5出来的,则它们可统一写成125 * x这样的形式

125*x<=1000,x<=8

能分解出来4个5出来的,则它们可统一写成625 * x这样的形式

625*x<=1000,x<=1

能分解出来5个5出来的,则它们可统一写成3125 * x这样的形式

3125*x<=1000,x <=0

于是当N=1000时,积累可提供200+40+8+1=249个5

/*
	version 5.0
*/

#include<bits/stdc++.h>
using namespace std;
int n,prime[1000002],tot,num[1000002],MOD=1000000007;
long long ans=1,k,ii,nn;
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++)	
		if(num[i]==0)
		{
			tot++;
			prime[tot]=i;
			for(int j=2;j<=n/i;j++)
				num[j*i]=1;	
		}
	for(int i=1;i<=n;i++)
		num[i]=i;	
	for(int i=1;i<=tot;i++)
	{
		k=0;
		ii=prime[i];
		nn=n;
		while(nn>=ii)
		{
			k+=nn/ii;
			ii*=prime[i];
		}
		ans=ans*(k+1)%MOD;
	}
	printf("%lld",ans);
	return 0;
}

总结如下

version 1.0 属于无脑枚举,好想好写,但只适用于小数据

version 2.0 依托因子成对的性质,也是枚举,但时间复杂度从O(N)降到O(根号N)

version 3.0 依托质因子分解+乘法原理的数学知识,免去很多循环

version 4.0 是3.0+加法原理

version 5.0 免去枚举1-n的数字,批量找出含质因子数字的个数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值