首页 > 其他分享 >约数个数统计问题探究

约数个数统计问题探究

时间:2023-02-27 09:13:18浏览次数:33  
标签:约数 int sum 个数 探究 using include

约数个数统计问题探究

               衡阳市第八中学 邹毅

约数个数统计问题是在算法学习过程 一个非常经典的问题,一般的描述为给出一个正整数N,请求出它的所有约数的个数,例如当N为6的时候,它的约数为1,2,3,6共4个。当N的范围不断变化,并且询问范围从单个数到某个区间进行变化时,解法趋于多样化,本文尝试进行一些归纳总结。

例题1:询问某个数字N的约数个数,N<= 1e6

解法:可从约数的定义出发,进行暴力求解即可,时间复杂度为O(N)

代码如下:

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

  

 

例题2:询问某个数字N的约数个数,N<= 1e9.

解法:发现N的约数总是成对成对的出现,例如当N=12时,可分解成如下形式:

 N=12=1*12=2*6=3*4

可将这些分解形式统一写成N=a*b,a<=b.于是只需要枚举a的范围,判断a是否为N的约数,如果满足条件的话,还可以同时得到另一个约数b=N/a.需要注意的是如果a=b的话,则只能被统计1次. 时间复杂度为O(sqrt(N))

代码如下:

 

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

  

一般来说,统计数字N的约数个数用上面这个算法即可,如果希望常数更小一点的话,可以利用数论知识进行优化,其对10^18之内的随机数据的表现还是很不错的。

方法如下:

1:利用惟一分解定理,对数字N进行质因子分解,例如12可分解成2^2*3^1。

2:使用乘法原理,如果数字x为12的约数,则x也必然可分解成2^a*3^b的形式,并且0<=a<=2,0<=b<=1,即a有3种选择,b有2种选择,x共有6种选择。

代码如下:

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	long long n,i,j,m,sum,ans=1;
	cin>>n;
	for(i=2; i*i<=n; i++) 
	{
		sum=0;
		while(n%i==0) 
		{
			n/=i;
			sum++;
		}
		ans*=(sum+1);
	}
	if(n!=1)ans*=2;
	cout<<ans<<endl;
	return 0;
}

  

  接下来我们尝试求解某个范围的所有数字的约数的总和。

例题3:

对于输入一个数字,输出其约数个数。例如10就有4个约数1,2,5,10 。这种问题,小可可已做得非常熟练了。现在老师将这个问题变了下:给出一个数字N,求1到N之间每个数字的约数个数,再输出其累加和

Input

一个数字N

N< =1e6

Output

如题

 

输入数据 1

3

输出数据 1

5

题解:可枚举数字N,如果套用前面例题2中的常规做法的法,将超时两个点,但使用例题2的优化算法的话,将顺利Ac.

用时2396ms,时间复杂度为O(2/3*N*sqrt(N))

代码如下:

 

#include<bits/stdc++.h>
using namespace std;
int dfs(int n)
{
	int sum=1;
	for(int i=2;i<=sqrt(n);i++)
	{
		int cnt=0;
		while(n%i==0)
		{
			cnt++;
			n/=i;
		}
		sum*=(cnt+1);
	}
	if(n>1) sum*=2;
	return sum;
}
int main()
{
	int n,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
		ans+=dfs(i);
	cout<<ans<<endl;
	return 0;
}

  

优化方法1:使用欧拉筛法进行状态之间的转移,用时1987ms,一般认为欧拉筛法的时间复杂度为O(N)的,但也有人认为是O(N*log2N)的。

代码如下:

 

#include <iostream>
using namespace std;
const int n = 1e7;
const int N = n + 1;
int m=0;
int a[N], b[N], c[N], d[N];
int f[N], g[N];
void init()
{
    f[1] = g[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (!a[i])
        {
            b[++m] = i;
            c[i] = 1, f[i] = 2;
        }
        for (int j = 1; j <=m && b[j] * i <= n; j++)
        {
            int k = b[j];
            a[i * k] = 1;
            if (i % k == 0)
            {
                c[i * k] = c[i] + 1;
                f[i * k] = (f[i] / (c[i]+1))* (c[i * k] + 1);
                break;
            }
            else
            {
                c[i * k] = 1;
                f[i * k] = 2 * f[i];
            }
        }
    }
}
   
int main()
{
   init();
    int x;
    cin>>x;
    int ans=0;
    for (int i=1;i<=x;i++)
         ans=ans+f[i];
    cout<<ans<<endl;
    return 0;
}

  

 

优化方法2:使用数论分段方法进行求解。

方法如下:枚举数字i做为约数,统计其在[1..N]有多少个倍数即N/i,然后将出现次数一样的约数进行合并统计,例如

当N=20,i=7时,易知7在1到20之间有2个倍数,分别为7,14,于是在统计7与14的约数时,i就将做出贡献。

同理当i=8,9,10时,它们也是出现2次的,于是可以进行合并统计,使用数学分析可知这样的分段个数至多为2*sqrt(N)个,于是时间复杂度为O(sqrt(N)),对于本题用时11ms,易可可轻松应对1e14这样的数据范围。

代码如下

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int main()
{
    ll n;
	 scanf("%lld",&n);
    ll res = 0;
    for(int i=1;i<=n;) {
        int r = n/(n/i)+1;
        res += (n/i) * (r - i);
        i = r;
    }
    printf("%lld\n",res);
    return 0;
}

  

优化方法3:使用容斥定理

方法如下:对于数字N,其可分解成N=i*j的形式,为了不重复统计,一般要求i<=j。于是可以枚举i,算出其出现次数并进行累加,最后将累加结果乘以2,得到一个结果。这个结果是没有考虑i与j的大小关系的,于是要从其中减去i>j的情况,并且将i=j的情况只能统计1次,统计分析可知要减去的值正好为int(sqrt(n))* int(sqrt(n))。时间复杂度为O(sqrt(N)),但胜在代码清爽至极,最终感悟是OI的尽头是数学。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,sum=0;
    cin>>n;
    int nn=sqrt(n); 
    for(int i=1;i<=n/i;i++)
        sum=sum+n/i;
    cout<<sum*2-nn*nn<<endl;
    return 0;
}

  

标签:约数,int,sum,个数,探究,using,include
From: https://www.cnblogs.com/oiteacher/p/17158487.html

相关文章