spoj LCMSUM sigma(lcm(i,n));


Problem code: LCMSUM

 

Given n, calculate the sum LCM(1,n) + LCM(2,n) + .. + LCM(n,n), where LCM(i,n) denotes the Least Common Multiple of the integers i and n.

Input


The first line contains T the number of test cases. Each of the next T lines contain an integer n.


Output


Output T lines, one for each test case, containing the required sum.

Example

Sample Input :
3
1
2
5

Sample Output :
1
4
55


Constraints

1 <= T <= 300000
1 <= n <= 1000000

 

题意:sigma(lcm(i,n)) ; 1<=i<=n

思路:题意倒是很简单,有类似的题目sigma(gcd(i,n)) ;1<=i<=n;

    一看就是果然是同类型的。

gcd(i,n)的题目 http://www.cnblogs.com/tom987690183/p/3247439.html

这个是求lcm()最小公倍数.

同样的思路,我们枚举gcd() = d 则在[ 1 , n ]中与n最大公约数为d的值有euler(n/d)个.

这些数的和为euler(n/d)*n/2;  我们知道lcm = x*n/(gcd(x,n)) = > x*n/d ;

因为与n gcd() = d的数字为x1,x2,x3....

根据lcm = x*n/(gcd(x,n)) 可以得到 (n/d)*(x1+x2+x3...) => [euler(n/d)*n/2]*(n*d);

这里需要注意的是当gcd(i,n) = n的时候,用euler(n)*n/2算的时候是不对的,就特判吧。

这样的思路,我们就可以试一下打欧拉表opl[ ],需要时间o(N);

然后对于n,要用sqrt(n)的时间。

T*sqrt(n)+o(N) = 3^8+10^6.果断超时。以为能ac,吼吼。

 

 1 #include<iostream>

 2 #include<stdio.h>

 3 #include<cstring>

 4 #include<cstdlib>

 5 using namespace std;

 6 typedef long long LL;

 7 

 8 const int maxn = 1e6+3;

 9 int opl[maxn];

10 void init()

11 {

12     for(int i=1;i<maxn;i++) opl[i] = i;

13     for(int i=2;i<maxn;i++)

14     {

15         if(opl[i]==i) opl[i] = i-1;

16         else continue;

17         for(int j=i+i;j<maxn;j=j+i)

18             opl[j] = opl[j]/i*(i-1);

19     }

20 }

21 int main()

22 {

23     int T;

24     LL n;

25     init();

26     scanf("%d",&T);

27     while(T--)

28     {

29         scanf("%lld",&n);

30         LL sum = 0;

31         for(LL i=1;i*i<=n;i++)

32         {

33             if(n%i==0)

34             {

35                 if(i!=n)

36                 sum = sum + (n/i)*(opl[n/i]*n/2);

37                 LL tmp = n/i;

38                 if(tmp!=i && tmp!=n)

39                     sum = sum + i*(opl[i]*n/2);

40             }

41         }

42         printf("%lld\n",sum+n);

43     }

44     return 0;

45 }
View Code

 

这样的话,是不行了。

原始相当于

也就能转化为(easy)

这样的话,我们就能单独的求出euler(a)*a/2;然后用它来筛选g[n]的值。

(x|n的意思代表 n是x的倍数)

我们想到在第一种方法里,我们用sqrt(n)来求它的因子的方法。

同理,逆向一下就好。

这样我们只需要o(N) 的时间对g[]进行筛选。总的预处理时间

3*o(N) = > 3*10^6; 

完毕。

需要注意的是,我们没有把gcd() = n的情况包含进去,所以最后要+n。

代码:

 1 #include<iostream>

 2 #include<stdio.h>

 3 #include<cstring>

 4 #include<cstdlib>

 5 using namespace std;

 6 typedef long long LL;

 7 

 8 const int maxn = 1e6+3;

 9 LL opl[maxn];

10 LL g[maxn];

11 void init()

12 {

13     for(int i=1;i<maxn;i++) opl[i] = i;

14     //这种方法筛选素数,不用和以前一样要先刷素数,还开num[];

15     for(int i=2;i<maxn;i++) 

16     {

17         if(opl[i]==i)

18             opl[i] = i-1;

19         else continue;

20         for(int j=i+i;j<maxn;j=j+i)

21             opl[j] = opl[j]/i*(i-1);

22     }

23     for(int i=2;i<maxn;i++){

24         opl[i] = opl[i]*i/2;

25         g[i] = opl[i];

26     }

27     for(long long i=2;i<=1000;i++) //这里的i 不能用int,肯定会超int的

28     {

29         for(long long j=i*i,k=i;j<maxn;j=j+i,k++)

30         if(i!=k) //不重复

31             g[j] = g[j] + opl[i]+opl[k];

32         else g[j] = g[j] + opl[i];

33     }

34 }

35 int main()

36 {

37     init();

38     int T;

39     LL n;

40     scanf("%d",&T);

41     while(T--)

42     {

43         scanf("%lld",&n);

44         printf("%lld\n",g[n]*n+n);

45     }

46     return 0;

47 }

 

你可能感兴趣的:(cms)