SPOJ LCMSUM & bzoj 2226 & 洛谷 1891 题解(数论,推式子)

原题链接:
SPOJ
bzoj
洛谷上的重题

题意简述

3 e 5 3e5 3e5组询问,每次给定一个 n ( n < = 1000000 ) n(n<=1000000) n(n<=1000000),求:
l c m ( 1 , n ) + l c m ( 2 , n ) . . . + l c m ( n , n ) lcm(1,n)+lcm(2,n)...+lcm(n,n) lcm(1,n)+lcm(2,n)...+lcm(n,n)

数据

输入
T //询问个数
n
n
...
n//每个n
输出
ans
ans
...
ans//对于每个询问,输出答案

思路

暴力推式子。来♂
还是设 g = g c d ( i , n ) g=gcd(i,n) g=gcd(i,n),注意不是常数,是宏定义,相当于#define
原 式 = ∑ i = 1 n l c m ( i , n ) = ∑ i = 1 n n i g = n ∑ i = 1 n i g ( 常 规 变 形 , 拆 l c m 为 g c d ) = n ∑ d ∣ n ∑ i = 1 n [ g = = d ] i d ( 枚 举 g c d 的 值 , 去 判 断 在 哪 些 i 中 被 算 到 ) = n ∑ d ∣ n ∑ i = 1 n [ g c d ( i d , n d ) = = 1 ] i d ( 括 号 里 面 除 以 d ) = n ∑ d ∣ n ∑ i = 1 n d [ g c d ( i , n d ) = = 1 ] i ( 把 i 的 定 义 换 成 : 枚 举 i d ) = n ∑ d ∣ n ∑ i = 1 d [ g c d ( i , d ) = = 1 ] i ( 因 为 我 们 枚 举 的 d 是 n 的 因 数 , 所 以 d 和 n / d 应 该 是 对 称 的 , 把 n / d 换 成 d , 只 是 枚 举 顺 序 反 了 一 下 , 不 改 变 答 案 ) 原式\\ =\sum\limits_{i=1}^{n}lcm(i,n)\\ =\sum\limits_{i=1}^{n}\frac{ni}{g}\\ =n\sum\limits_{i=1}^{n}\frac{i}{g}\\ (常规变形,拆lcm为gcd)\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{n}[g==d]\frac{i}{d}\\ (枚举gcd的值,去判断在哪些i中被算到)\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{n}[gcd(\frac{i}{d},\frac{n}{d})==1]\frac{i}{d}\\ (括号里面除以d)\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{\frac{n}{d}}[gcd(i,\frac{n}{d})==1]i\\ (把i的定义换成:枚举\frac{i}{d})\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{d}[gcd(i,d)==1]i\\ (因为我们枚举的d是n的因数,所以d和n/d应该是对称的,把n/d换成d,只是枚举顺序反了一下,不改变答案) =i=1nlcm(i,n)=i=1ngni=ni=1ngilcmgcd=ndni=1n[g==d]digcdi=ndni=1n[gcd(di,dn)==1]di(d)=ndni=1dn[gcd(i,dn)==1]iidi=ndni=1d[gcd(i,d)==1]idndn/dn/dd
现在我们要求这样一个东西:
对于任意一个 < = n <=n <=n的数 x x x,求 [ 1 , x ] [1,x] [1,x]中所有和 x x x互质的数的和。
我们能求什么呢?
[ 1 , x ] [1,x] [1,x]中所有和 x x x互质的数的个数。很明显这个就是 ϕ ( x ) \phi(x) ϕ(x)
珂是。。。如果变成求和。。。怎么办呢?好像不太好求。
我们回想一下高斯是怎么算 [ 1 , 100 ] [1,100] [1,100]的和的。显然他知道 [ 1 , 100 ] [1,100] [1,100]里面的个数( 100 100 100个)。然后他很聪明,把序列反过来写一遍,发现和相等,这样和就是 100 ∗ 101 / 2 100*101/2 100101/2
珂是我为什么要讲高斯的故事呢?因为这个和高斯很像。我们不妨设 x = 20 x=20 x=20,观察一下和 20 20 20互质的数,把逆序,逆序+正序,都写在下面:
1 ,3 , 7, 9,11,13,17,19
19,17,13,11, 9, 7, 3, 1
20,20,20,20,20,20,20,20
发现了什么?当然,如果你把 x x x代入别的验证,也是这样的。这就是我们发现的规律。

此时,两个序列的和就是 n ∗ ϕ ( n ) n*\phi(n) nϕ(n)。显然,两个序列的和都是相等的。那么其中一个序列的和就是: n ∗ ϕ ( n ) 2 \frac{n*\phi(n)}{2} 2nϕ(n)。也就是我们要求的那个东西。
(不过要特判一个 n = 1 n=1 n=1,此时的和为 1 1 1,而不是 n ∗ p h i ( n ) / 2 = 1 2 n*phi(n)/2=\frac{1}{2} nphi(n)/2=21

还记得我们刚刚的答案式子是什么吗?是 n ∑ d ∣ n ∑ i = 1 d [ g c d ( i , d ) = = 1 ] i n\sum\limits_{d|n}\sum\limits_{i=1}^{d}[gcd(i,d)==1]i ndni=1d[gcd(i,d)==1]i。然后我们现在珂以把后面优化掉了,变成:
n ∑ d ∣ n d ∗ ϕ ( d ) 2 n\sum\limits_{d|n}\frac{d*\phi(d)}{2} ndn2dϕ(d)
至此,我们珂以每个询问 O ( n ) O(\sqrt{n}) O(n )加特技,卡常数,水过这个题。
当然,我们也有更快的方法。考虑预处理。设一个数组,一开始都是 0 0 0。枚举每个 d d d d d d的倍数,在 d d d的倍数上加上 d ∗ ϕ ( d ) 2 \frac{d*\phi(d)}{2} 2dϕ(d)的值(特判 d = 1 d=1 d=1)。虽然这样预处理是 O ( n l o g n ) O(nlogn) O(nlogn)的(调和级数),但是每次询问就珂以 O ( 1 ) O(1) O(1)出来了。相比刚刚那个算法,虽然多耗了一点思考,但是省了很多卡常数的力气,珂以说是优秀的三好算法了。

三好算法)代码:

#include
#define N 1001000

using namespace std;

bool notp[N];int primes[N],phi[N];
long long f[N];

void Init()
{
    int& cnt=primes[0];
    int n=1000000;

    notp[1]=1;
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if (!notp[i])
        {
            primes[++cnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt and i*primes[j]<=n;j++)
        {
            int u=primes[j];
            notp[i*u]=1;
            if (i%u!=0)
            {
                phi[i*u]=phi[i]*phi[u];
            }
            else
            {
                phi[i*u]=phi[i]*u;
                break;
            }
        }
    }//预处理phi

    for(int i=1;i<=n;i++)
    {
        for(int j=1;i*j<=n;j++)
        {
            f[i*j]+=(i==1?1:1ll*phi[i]*i/2);
        }
    }//预处理,O(nlogn)
}

int n;

int main()
{
    Init();
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        printf("%lld\n",f[n]*1ll*n);
    }
    return 0;
}
//代码是很久之前写的,好久不整理博客了。所以码风有点。。。。。。。
//虽然常数小,但也是卡了一些常数,比如我没有全部用long long

回到总题解界面

你可能感兴趣的:(SPOJ,bzoj,洛谷)