http://acm.hdu.edu.cn/showproblem.php?pid=6134
题目大意:给你一个n,求 f(n)=∑ni=1∑ij=1⌈ij⌉其中gcd(i,j)=1 。
解题思路:根据已知公式,可以令 h(i)=∑ij=1⌈ij⌉gcd(i,j)=1 .
令 g(i)=∑ij=1⌈ij⌉
而 f(n)=∑ni=1h(i)
设gcd(i,j) = d,明显可以知道d能被i整除,当然也可以找到一个k,使得k*d = j,则我们可以得到gcd(i,kd) = d; 我们可以构造 ∑i/dk=1⌈ikd⌉gcd(i,kd)=d 化简可得 ∑i/dk=1⌈i/dk⌉gcd(i/d,k)=1 即原式等于h(i/d),当d取满i所有的约数则可以得到 g(i)=∑d|ih(i/d) 根据约数定理可以化简得 g(i)=∑d|ih(d) 根据莫比乌斯反演可以得到 h(i)=∑d|iμ(d)g(i/d)
现在我们的任务就是如何求h(i)了,那就要求 μ(d) 和 g(i/d);
μ(d) 可以根据线性筛法来求。
令t = i/d;则当 j = t时g(t) = 1 当 t/2<= j< t 时 g(t) = 2 当 t/3<=j < t/2 时 g(t) = 3 由此可以看出在t这个间隔内g(t)的值不会发生变化。这里可以用差分的思想来求g(t)。
具体代码如下:
#include
#include
#include
const int mod = 1e9+7;
const int N = 1e6;
using namespace std;
int mu[N+10],vis[N+10],prime[N+10];
int sum[N+10],g[N];
int n,m;
void get_mu()///线性筛法求mu
{
int cnt = 0;
memset(mu,0,sizeof(mu));///先让初始值全部赋值为0
mu[1] = 1;///第一个元素为1
memset(vis,0,sizeof(vis));
vis[0] = vis[1] = 1;
for(int i=2;i<=N;i++){
if(!vis[i]){
prime[cnt++] = i;
mu[i] = -1;///如果这个数是个素数的话则mu【i】 = -1;
}
for(int j = 0;j < cnt&& i*prime[j]<=N;j++){
vis[i*prime[j]] = 1;
if(i%prime[j])///控制这个数由几个不同素数组成
mu[i*prime[j]] = -mu[i];
else{///表示有相同素数组成
mu[i*prime[j]] = 0;
break;
}
}
}
}
void work()
{
memset(sum,0,sizeof(sum));
memset(g,0,sizeof(g));
get_mu();
for(int i=1;i<=N;i++){///求g(i)
g[i]++;
for(int j = i; j <= N; j+=i)
g[j+1]++;
}
for(int i=1;i<=N;i++)
g[i] = (g[i-1] + g[i])%mod;
for(int i = 1; i <= N; i++){///求h(i)
if(mu[i] == 0) continue;
for(int j = i; j <= N; j+=i){
if(mu[i] > 0)sum[j] = (sum[j] + g[j/i])%mod;
else sum[j] = (sum[j] - g[j/i]+mod)%mod;
}
}
for(int i=1;i<=N;i++)
sum[i] = (sum[i] + sum[i-1])%mod;
}
int main()
{
int n;
work();
// for(int i=1;i<=10;i++)
// cout<
// cout<
// for(int i=1;i<=10;i++)
// cout<
// cout<
// for(int i=1;i<=10;i++)
// cout<
// cout<
while(~scanf("%d",&n)){
printf("%d\n",sum[n]);
}
return 0;
}