P3801分解质因数
问题描述
记Pi表示正整数i的质因数集合。
已知正整数n,求满足下列条件的有序正整数对(a,b)的数目:
(1)1<=a<=b<=n
(2)t为a,b的最大公约数,Pt是Pn的子集
输入格式
一个正整数n.
输出格式
一个正整数,表示合题意的有序正整数对的数目.
样例输入 1
6
样例输出 1
20
样例输入 2
7
样例输出 2
19
数据范围
50%的数据1<=n<=2000;
100%的数据1<=n<=1000000.
此题有两种做法。
做法一:先介绍一种比较简单粗暴的。
我们考虑一个数
d
,满足
总之,我们求出了
d
,那么现在需要考虑对于每一个
那么显然我们只需要找出在
1−nd
的范围中,有多少对互质的整数即可,那么显然是
∑ndi=1ϕ(i)
,那么
ϕ
的前缀和
Sϕ
可以用线性筛处理,然后枚举
d
把
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define N 1000005
#define ll long long
using namespace std;
ll n,phi[N],S[N],P[N],tot,ans;
bool mark[N];
int main()
{
ll i,j;
scanf("%lld",&n);phi[1]=1;S[1]=1;
for(i=2;i<=n;i++)
{
if(!phi[i])phi[i]=i-1,P[++tot]=i;
for(j=1;j<=tot&&P[j]*i<=n;j++)
if(i%P[j])phi[i*P[j]]=phi[i]*(P[j]-1);
else {phi[i*P[j]]=phi[i]*P[j];break;}
S[i]=S[i-1]+phi[i];
}
for(i=1;i<=tot;i++)if(n%P[i])for(j=P[i];j<=n;j+=P[i])mark[j]=1;
for(i=1;i<=n;i++)if(!mark[i])ans+=S[n/i];
printf("%lld",ans);
}
做法二:
做法二比较的巧妙,我们可以认为在
Pn
中的质数不是质数,那么题设条件就变成了求有多少对互质的数对,那么显然是欧拉函数求解,但是由于质数是改动过的,我们记他为
ϕ′(x)
,那么因为这个函数与欧拉函数基本是一样的,那么他显然是积性函数,那么就可以用线性筛来搞。
具体来说,我们在普通的线性筛中,遇到质数时判断一下,如果这个质数在
Pn
中,那么
ϕ′(x)=x
,否则
ϕ′(x)=x−1
,然后合数还是通过积性函数性质来搞。
代码:
#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;
const int N=20000050;
int n,phi[N],prime[N];
bool mark[N],p[N];
void linear()
{
int i,j,tot=0; phi[1]=1;
for(i=2;i<=n;i++)
{
if(!phi[i])
{
if(!p[i])phi[i]=i-1;
else phi[i]=i;
prime[++tot]=i;
}
for(j=1;j<=tot&&prime[j]*i<=n;j++)
if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
else phi[i*prime[j]]=phi[i]*(phi[prime[j]]);
}
}
main()
{
int i,j,k;
cin>>n;
int tmp=n;
for(i=2;i*i<=n&&tmp!=1;i++)
if(tmp%i==0)
{
while(tmp%i==0)tmp/=i;
p[i]=true;
}
if(tmp!=1)p[tmp]=true;
linear();
long long ans=0;
for(i=1;i<=n;i++)ans+=1LL*(phi[i]);
cout<<ans;
}