CH3101 质数分解的灵活处理
思路
直接对N!分解肯定不行的,想到N!的最大因子就是N,并且N!的所有因子就是1-N中所有数的因子之和,对1-N中每一个数进行质因数分解肯定也是不现实的,因此转移变量,对于每一个数考虑因子太慢,不如考虑每个因子对于所有数的个数,对于因子p,[1,N]中至少只有一个p因子的数有[N/p]个
,对于含有p^2的因子的数有[N/p]个,本来两个因子应该被计两次数,但是至少一个因子时计过了这部分数,因此不用乘2,我们易得对于一个因子p,N!中的个数为:
这里所有的[]表示高斯函数,显然可以看出只需要O(logn)的时间,因此总体加上筛质数的时间总共是O(nloglogn+nlogn)(这里也可以直接筛到1e6)
注意事项
1)这里对prime存储之后一定要上限设置,即i<prime.size(),不能只用prime[i]<=n,否则会接着向后走发生除0错误
2)这里原书作者用了一个写法for (int j = n; j; j /= p) c += j / p来对p的个数进行计算,代替了每一次乘p+判断,妙!注意:p,p^2 ……, p^m, n,序列与n/q,n/q^2, ……,n/q^m 生成的序列是一样的,只不过倒序,或者说,把n等价于q^m次方,这样算等价于 q^ m/q^ i
复习反省
绝了,声明数组忘了大小忘了+1,正好num是在used后面声明的,然后改used的时候改到maxn下标时,数组越界但不报错,剩下的这个地址就是num的(变量赋地址的连续性),正好把num改了……质数表就错了
代码
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e6;
int used[maxn+1];
typedef long long ll;
vector<ll> prime;
ll num = 0;
void eratothenes()
{
memset(used,0,sizeof(used));
for(int i = 2;i*i <=maxn;i++)
{
if(!used[i])
{
for(int j = 2;j*i<=maxn;j++)
{
used[j*i] = 1;
}
}
}
for(int i = 2;i<=maxn;i++)
if(!used[i]) prime.push_back(i);
}
int main()
{
eratothenes();
ll tmp,note,sum;
ll n;
cin >> n;
if(n== 1) cout << 1 <<' '<<1<<endl;
else
{
for(int i = 0;i<prime.size()&&prime[i] <=n;i++)
{
note = prime[i];
tmp = note;
sum = 0;
for(int j = 1;tmp<=n;j++)
{
sum+=n/tmp;
tmp *=note;
}
cout << note <<' '<< sum<<endl;
}
}
system("pause");
}