题目描述
链接:https://ac.nowcoder.com/acm/contest/3004/H
来源:牛客网
约数个数定理
百度百科链接:
https://baike.baidu.com/item/约数个数定理/4926961?fr=aladdin
对于一个大于1正整数n可以分解质因数:
n = p1a1 · p2a2· p3a3· p4a4······pnan
则n的正约数的个数就是:
f(n) = (a1+1)(a2+1)(a3+1)(a4+1)······(an+1)
其中a1、a2、a3、a4、…an是p1、p2、p3、p4、…pn的指数
f(n) = (a1+1)(a2+1)(a3+1)(a4+1)······(an+1)的证明就是乘法原理
例如:
36的因数有9个为:
1 2 3 4 6 9 13 18 36
36=4·9=(22)(32)
f(36)=(2+1)(2+1)=9
个人理解:
分解质因数后可得:
n = p1a1 · p2a2· p3a3· p4a4······pnan(式一)
若p1是n的一个因数
则有:
p10 是n的一个因数 n%p10 ==0
p11 是n的一个因数 n%p11==0
·
·
·
p1a1是n的一个因数 n%p1a1==0
对于p2、p3……pn都满足上述
因此:
任意a1’(0到a1)个p1
任意a2’(0到a2)个p2
任意a3’(0到a3)个p3
·
·
·
任意an’(0到an)个pn
(包括0个)
相乘得:
p1a1’×p2a2’×p3a3’×……×pnan’(式二)
都是n的因数
即(式一)%(式二)==0 (可以化简得出)
代码实现
1.素数打表
运算过程中需要用到素数表
prime[n]
可以用素数打表实现
链接:
https://blog.csdn.net/harington/article/details/86571150
2.枚举得到an
通过枚举得an,枚举过程类似于素数打表。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL const mod = 1e9+5;
#define N 1000005
bool isPrime[N];
int prime[N];
int tot;
void get_prime()
{
for(int i=2;i<N;i++){
if(!isPrime[i]){
prime[tot++] = i;
}
for(int j=0;j<tot;j++){
if(prime[j] * i >= N) break;
isPrime[prime[j] * i] = 1;
if(i%prime[j] == 0) break;
}
}
}//素数打表
LL num[N];
LL ans[N][2]; //一维是所有因数,二维是质因数个数
int main()
{
get_prime();
LL n,m;
scanf("%lld%lld",&n,&m);
LL k[m];
for(int i=0;i<m;i++) scanf("%lld",&k[i]);
LL K[n+1];
memset(K,0,sizeof(K));
for(LL i=2;i<=n;i++) ans[i][0] = 1,num[i] = i,ans[i][1]=1;
for(int i=0;i<tot;i++){
if(prime[i] > n) break;
for(LL j=prime[i];j<=n;j+=prime[i]){
LL cot = 0;
while(num[j]%prime[i]==0){
cot++;
num[j] /= prime[i];
}
if(cot) ans[j][0] *= (cot + 1) %mod ;
ans[j] [0]%= mod;
ans[j][1]++;
}
}
for(int i=2;i<=n;i++)
{
ans[i][0]-=ans[i][1];
K[ans[i][0]]++;
}
for(int i=0;i<m;i++)
{
printf("%lld\n",K[k[i]]);
}
}