UVA

来源:互联网 发布:全国地区数据库 编辑:程序博客网 时间:2024/05/29 15:13


UVA - 11426
题意:

N1i=1Nj=i+1gcd(i,j) (N<=4e6, 1 <= i < j <= N)

思路:

因为有两个变的数i,j所以我们考虑固定一个,设为n.那么我们只要分别求出gcd(i,n) == d 的有多少对,那么贡献就是cnt*d.
上面这个和设为f(n) = gcd(1,n)+gcd(2,n) ….+gcd(n-1,n)。

考虑怎么求f(n)。gcd(i,n) = d 可以转化为 gcd(i/d,n/d) = 1。进一步我们可以想到满足gcd(i/d,n/d)=1的对数,就是和n/d互质的数的个数,也就是φ(n/d),所以这一步的贡献就是 d*φ(n/d),f(n)就是要枚举所有n的因子.
得到上面这个东西我们肯定要枚举gcd了,然后利用欧拉筛的方法去预处理k*d,复杂度为调和级数O(nlogn)
但是我们这样只是求出每个f(n),那么在设s(n)为我们所要求的和.
s(n) = f(1)+f(2)….f(n) = s(n-1)+f(n). 预处理出所有的f(n)得到所有s(n)即可.

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 5e6+7;vector<int>prime;int phi[maxn];ll f[maxn],s[maxn];int n;void init(){    phi[1] = 1;    for(int i = 2;i < maxn;++i)    {        if(!phi[i])        {            prime.push_back(i);            phi[i] = i - 1;        }        for(int j = 0;j < prime.size() && prime[j] * i < maxn;++j)        {            if(i % prime[j] == 0)            {                phi[i*prime[j]] = phi[i] * prime[j];                break;            }            else                phi[i*prime[j]] = phi[i]*(prime[j] - 1);        }    }}void solve(){    memset(f,0,sizeof f);    memset(s,0,sizeof s);    for(int i = 1;i < maxn;++i)        for(int j = i + i;j < maxn; j += i)        f[j] += i * phi[j/i];    for(int i = 2;i < maxn;++i)    s[i] = s[i-1] + f[i];    return ;}int main(){    init();    solve();    while(~scanf("%d",&n))    {        if(n == 0)        break;        printf("%lld\n",s[n]);    }    return 0;}