唯一分解定理:
任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积N=P1a1P2a2P3a3......Pnan,这里P1<P2<P3......<Pn均为质数,其中指数ai是正整数。这样的分解称为 N 的标准分解式。
大概意思就是说可把一个数写成素数乘积的形式,例如:
12= 2^2 * 3^1 , 42= 2^1 * 3^1 * 7^1 , 18= 2^1 * 3^2 ........等等;
所以就必须要用到素数筛,不会欧拉筛(线性筛)的可以先去网上学一下。
·素数筛:
代码:
void get_pr()
{
for(int i=2;i<=N;i++)
{
if(!st[i])pr[len++]=i;
for(int j=0;j<len&&pr[j]<=N/i;j++)
{
st[pr[j]*i]=1;
if(i%pr[j]==0)break;
}
}
}
注意事项:
素数数组一般不开到1e7,状态标记数组一般要开大一些,防止数组越界。
·求因子总数:
原理很简单,就是遍历从最小的素数开始除,记录每个素因子的个数,因为这里不需要求每一个质因子,所以就不开数组存了,直接记录完每一个素数个数直接运算........
代码:
int geta_1(ll n)
{
int ans=1;
for(int i=0;i<len&&pr[i]*pr[i]<=n;i++)
{
int x=0;
while(n%pr[i]==0)
{
n=n/pr[i];
x++;
}
ans*=(x+1);
}
if(n>1)ans*=2;
return ans;
}
配合素数筛使用即可。
完整代码:
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6;
int pr[N],st[N*5],len=0;
void get_pr()
{
for(int i=2;i<=N;i++)
{
if(!st[i])pr[len++]=i;
for(int j=0;j<len&&pr[j]<=N/i;j++)
{
st[pr[j]*i]=1;
if(i%pr[j]==0)break;
}
}
}
ll geta(ll n)
{
ll ans=1;
for(ll i=0;i<len&&pr[i]*pr[i]<=n;i++)
{
ll x=0;
while(n%pr[i]==0)
{
x++;
n=n/pr[i];
}
ans=ans*(x+1);
}
if(n>1)ans*=2;
return ans;
}
int main()
{
get_pr();
int t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
printf("%lld\n",geta(n));
}
return 0;
}
质因子分解(求每个质因子的个数):
原理和上面的一样,只是开了一个数组存一下,这里结合洛谷上的一个题来将来讲。
·例题:
P2043 质因子分解
题目描述
对 N! 进行质因子分解。
输入格式
输入数据仅有一行包含一个正整数 N,N≤10000。
输出格式
输出数据包含若干行,每行两个正整数 p,a,中间用一个空格隔开。表示 N! 包含 a 个质因子 p,要求按 p 的值从小到大输出。
输入输出样例
输入
10
输出
2 8 3 4 5 2 7 1
说明/提示
10!=3628800=(28)×(34)×(52)×710!=3628800=(28)×(34)×(52)×7。
·分析:
因为阶乘的数非常大,先算阶乘再求答案显然不可能,那就把阶乘拆开来算
代码:
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6;
int pr[N],st[N*2],len=0,f[N];
void get_pr()
{
for(int i=2;i<=N;i++)
{
if(!st[i])pr[len++]=i;
for(int j=0;j<len&&pr[j]<=N/i;j++)
{
st[pr[j]*i]=1;
if(i%pr[j]==0)break;
}
}
}
void geta(int n)
{
for(int i=0;i<len&&pr[i]*pr[i]<=n;i++)
{
while(n%pr[i]==0)
{
n=n/pr[i];
f[pr[i]]++;
}
}
if(n>1)f[n]++;
}
int main()
{
get_pr();
int n;
cin>>n;
for(int i=1;i<=n;i++)geta(i);
for(int i=0;i<len;i++)
{
if(f[pr[i]]>0)
{
printf("%d %d\n",pr[i],f[pr[i]]);
}
}
return 0;
}
小结:
当然肯定还有其他方法,我这里是用桶排数组的思想直接存个数,因为这里素数不会特别大所以刚好合适,其实也可以存素数的下标,不过这里不需要。
总结:
唯一分解定理应用还是非常广泛的,所以还是有必要掌握的。对于我这个不是数学专业的人来说,一切的数学公式和定理都不过是解决问题的工具,不必深究原理,目的只有一个,能解决问题就行。
!~~~小趴菜努力学习中ing........