SPOJ LCMSUM (BZOJ 2226) 求n以内所有数与n的lcm之和(保证所有人都能看懂)

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

保证所有人都能看懂,保证你只要会筛欧拉函数就能AC。

Catalog

Problem:传送门

Portal
 原题目描述在最下面。
T ( 300000 ) T(300000) T(300000)组数据,每次求 n ( 1000000 ) n(1000000) n(1000000)以内所有数与 n n n l c m lcm lcm之和。

Solution:

 这种数据,对于我这种大常数玩家来说是真的不友好,太难受了,想卡卡不过去。。。


 回归正题: a n s = ∑ i = 1 n L C M ( i , n ) = ∑ i = 1 n i × n g c d ( i , n ) = n × ∑ i = 1 n i g c d ( i , n ) ans = \sum_{i=1}^{n} LCM(i,n)= \sum_{i=1}^{n}\frac {i\times n}{gcd(i,n)}=n\times \sum_{i=1}^{n}\frac {i}{gcd(i,n)} ans=i=1nLCM(i,n)=i=1ngcd(i,n)i×n=n×i=1ngcd(i,n)i


 首先 g c d ( i , n ) gcd(i,n) gcd(i,n)的取值一定会取遍 n n n的所有因子,然后我们把所有和 n n n g c d gcd gcd相同的数字一起求和,就会得到下面这个式子:

n × ∑ d ∣ n ∑ i = 1 n i × [ g c d ( i , n ) = = d ] d n\times \sum_{d|n} \frac{\sum_{i=1}^{n}i\times [gcd(i,n) == d]}{d} n×dndi=1ni×[gcd(i,n)==d]
 再来看这个,若 g c d ( i , n ) = d gcd(i,n)=d gcd(i,n)=d,那么一定有 g c d ( i d , n d ) = 1 gcd(\frac{i}{d},\frac{n}{d})=1 gcd(di,dn)=1


n n n以内和 n n n g c d gcd gcd d d d的数一定是: d , d × 2 , . . . , d × n d d,d\times 2,...,d\times \frac{n}{d} d,d×2,...,d×dn。有 g c d ( d × x , n ) = d ,      x ≤ n d gcd(d\times x, n) = d,\;\;x\leq \frac{n}{d} gcd(d×x,n)=d,xdn,由这个可以等价地化简为 g c d ( x , n d ) = 1      x ≤ n d gcd(x,\frac{n}{d})=1\;\;x\leq \frac{n}{d} gcd(x,dn)=1xdn,然后可以进一步化简答案为:

n × ∑ d ∣ n ∑ i = 1 n d i × [ g c d ( i , n d ) = = 1 ] n\times \sum_{d|n} \sum_{i=1}^{\frac{n}{d}}i\times [gcd(i,\frac{n}{d}) == 1] n×dni=1dni×[gcd(i,dn)==1]
 再看,第一个求和里面的式子不就是 x x x以内所有与 x x x互质的数之和吗?答案更加简单了。定义 n n n与其互质数之和为 f ( n ) f(n) f(n)


n × ∑ d ∣ n ∑ i = 1 d i × [ g c d ( i , d ) = = 1 ] = n × ∑ d ∣ n f ( d ) n\times \sum_{d|n} \sum_{i=1}^{d}i\times [gcd(i,d) == 1]=n\times \sum_{d|n}f(d) n×dni=1di×[gcd(i,d)==1]=n×dnf(d)
 通过: g c d ( n , m ) = 1 , 那 么 g c d ( n , n − m ) = 1 gcd(n,m)=1,那么gcd(n,n-m)=1 gcd(n,m)=1,gcd(n,nm)=1,我们可以很轻松地得到下面公式:


 当 n = 1 n=1 n=1时, f ( n ) = 1 f(n)=1 f(n)=1;当 n ≥ 2 n\geq 2 n2时, f ( n ) = n × ϕ ( n ) 2 f(n)=\frac{n\times \phi(n)}{2} f(n)=2n×ϕ(n) ϕ ( n ) 是 n 的 欧 拉 函 数 的 值 \phi(n)是n的欧拉函数的值 ϕ(n)n


 我们可以很轻松每次以 O ( n ) O(\sqrt n) O(n )算出答案,但是很抱歉这样刚刚好超时了,当然如果你的常数很小,说不定可以过。。。。。那我们就只能通过预处理来解决问题了。


 有两种计数方法:第一个 f o r for for枚举各种因子,第二个 f o r for for枚举它的倍数;第一个 f o r for for枚举所有 ≤ ( M A X ) \leq \sqrt(MAX) ( MAX)的因子,第二个 f o r for for枚举另一个因子。

  • 法1:
for(int i = 1; i <= N; ++i) {//i是因子
    for(int j = i; j <= N; j += i) {//显然这里i是j的因子,直接给dp[j]累加上f(i)*j即可
        if(i == 1) dp[j] += j;
        else dp[j] += i * phi[i] / 2 * j;
    }
}
  • 法2:
//计算i的因子是1和i本身的情况
for(int i = 1; i <= N; ++i) dp[i] = i + i * phi[i] / 2 * i;
//i是第一个因子,j是第二个因子
for(int i = 2; i * i <= N; ++i) {
    dp[i*i] += i * phi[i] / 2 * i * i;//对于完全平方数的情况,累加上f(i)*(i*i)
    for(int j = i + 1; j * i <= N; ++j) {//对于其他的组合情况,累加上[f(i)+f(j)]*(i*j)
        dp[i*j] += (i * phi[i] / 2 + j * phi[j] / 2 ) * i * j;
    }
}

好了,这个题就解决了,也没什么难理解的吧。。。


写这个题题解的原因了,就是去年寒训的时候,被这种题虐哭了,,,今年给新生爽爽。。嘻嘻,然后想写一篇最易理解的题解给去年的自己看。

AC_Code:
#include<bits/stdc++.h>
namespace lh {
#define o2(x) (x)*(x)
    using namespace std;
    typedef long long LL;
    typedef unsigned long long uLL;
    typedef pair<LL, LL> pii;
}

using namespace lh;

const int MXN = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const int mod = 2019;

int x;
int pp[MXN], noprime[MXN], pcnt;
LL phi[MXN];
void get_prime() {
    noprime[0] = noprime[1] = 1;
    phi[1] = 1;
    for(int i = 2; i < MXN; ++i) {
        if(!noprime[i]) pp[pcnt++] = i, phi[i] = i-1;
        for(int j = 0; j < pcnt && pp[j] * i < MXN; ++j) {
            noprime[pp[j] * i] = 1;
            if(i % pp[j] == 0) {
                phi[i * pp[j]] = phi[i] * pp[j];
                break;
            }
            phi[i * pp[j]] = phi[i] * (pp[j]-1);
        }
    }
}
LL dp[MXN];
/*
n >= 2时,n以内与其互质数之和为n*phi(n)/2
暴力点,nsqrt(n)暴力所有情况
可以第一个for枚举各种因子,第二个for枚举它的倍数
或者
第一维枚举所有小于等于sqrt(MAX)的因子,第二维枚举另一个因子
*/
void init1(int N) {
    for(int i = 1; i <= N; ++i) {
        for(int j = i; j <= N; j += i) {
            if(i == 1) dp[j] += j;
            else dp[j] += i * phi[i] / 2 * j;
        }
    }
}
void init2(int N) {
    for(int i = 1; i <= N; ++i) dp[i] = i + i * phi[i] / 2 * i;
    for(int i = 2; i * i <= N; ++i) {
        dp[i*i] += i * phi[i] / 2 * i * i;
        for(int j = i + 1; j * i <= N; ++j) {
            dp[i*j] += (i * phi[i] / 2 + j * phi[j] / 2 ) * i * j;
        }
    }
}
int main() {
    get_prime();
    init1(1000001);//init2(1000001);
    int tim; scanf("%d", &tim);
    while(tim --) {
        scanf("%d", &x);
        printf("%lld\n", dp[x]);
    }
    return 0;
}

Problem Description:

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值