题目传送门:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3881
题目分析:又是一道神题。别的算法都是做一题少一题,只有数论是做一题多一题的……至于推导过程的话已经有神犇在网上放了很优秀的题解,我只是过来总结一下思路要点+贴代码而已。
要点:本题要求的是一个很奇怪的函数
f(n)=rad(n)ϕ(nrad(n))
,这肯定无法直接化简。于是我们首先证明它是个积性函数,然后用积性函数的性质
f(n)=∏ti=1f(pkii)
将它拆开。接下来我们将这个拆开后的式子带进g函数里面化简,得到
g(n)=∏ti=1(1+pi∑ki−1j=0ϕ(pji))
。然后我们发现
∑ki−1j=0ϕ(pji)=∑d|pki−1iϕ(d)=pki−1i
。于是就变成了
g(n)=∏ti=1(1+pkii)
。我们再根据对其每个质因数的限制条件,转化成枚举因数并限制其gcd的形式,变成了
g(n)=∑d|n[(d,nd)=1]d
(这很像SDOI2015约数个数和这题)。接下来就是很常规的化简和莫比乌斯反演。这题这也为我以后做其他数论题提供了一种新的思路吧。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1000010;
const long long M=1e9+9;
typedef long long LL;
LL f[maxn];
bool vis[maxn];
int prime[maxn];
int cur=0;
LL n=0;
void Make()
{
f[1]=1;
for (int i=2; i<maxn; i++)
{
if (!vis[i]) f[i]=-1,prime[++cur]=i;
for (int j=1; j<=cur && i*prime[j]<maxn; j++)
{
int k=i*prime[j];
vis[k]=true;
if (i%prime[j]) f[k]=-f[i];
else
{
f[k]=0;
break;
}
}
}
for (int i=2; i<maxn; i++)
{
f[i]*=i;
if (f[i]<0) f[i]+=M;
}
}
LL Sum(LL x)
{
LL y=x+1LL;
if (x&1) y>>=1;
else x>>=1;
return (x%M)*(y%M)%M;
}
LL S(LL x)
{
LL temp=0,last;
for (LL i=1; i<=x; i=last+1)
{
last=x/(x/i);
temp=(temp+ x/i%M*( Sum(last)-Sum(i-1)+M ) )%M;
}
return temp;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
Make();
while ( scanf("%lld",&n),n )
{
LL sn=(long long)floor( sqrt( (double)n )+1e-7 );
LL ans=0;
for (LL i=1; i<=sn; i++)
ans=(ans+ f[i]*S(n/i/i) )%M;
printf("%lld\n",ans);
n=0;
}
return 0;
}