BZOJ 2226: [Spoj 5971] LCMSum(数论+欧拉函数)

题目描述

传送门

题目大意:问你1到n中的数和n的lcm之和,T组询问。T <= 300000,n <= 1000000。


思路

一看就是比较套路的题。随便化简一下,大概是这样

i=1n[n,i]=i=1nni(n,i)=ni=1nd=1n[(i,n)=d]id

然后先枚举 d (只能是n的约数),令 i=id (将 i 看成id),变成

nd|ni=1nd[(i,nd)=1]i

接下来还要不要 反演呢?其实到这里已经大功告成了。根据我不知道从哪里偷来的公式 ni=1[(i,n)=1]i=φ(n)n+[n=1]2 ,答案就是
nd|nφ(d)d+[d=1]2

至于这个公式怎么证?显然。(证它干嘛呢)

于是总时间就变成了 O(n+Tn) ,理论跑不过,但政治书上说,理论要联系实际。


代码

#include <bits/stdc++.h>
#define maxn 1000100
#define temp (i * prime[j])
using namespace std;

typedef long long LL;
int n, T, cnt;
int prime[maxn], phi[maxn];
bool Vis[maxn];
LL ans;

void Da(){
    phi[1] = 1;
    for(int i = 2; i < maxn; i++){
        if(!Vis[i]){
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && temp < maxn; j++){
            Vis[temp] = true;
            if(i % prime[j] == 0){
                phi[temp] = phi[i] * prime[j];
                break;
            }
            else  phi[temp] = phi[i] * (prime[j] - 1);
        }
    }
}

int main(){

    scanf("%d", &T);

    Da();

    while(T --){
        scanf("%d", &n);
        ans = 0LL;
        for(int i = 1; i * i <= n; i++)
            if(n % i == 0){
                ans += (1LL * phi[i] * i + ((i == 1) ? 1 : 0)) >> 1LL;
                if(n / i != i)  ans += (1LL * phi[n/i] * (n/i)) >> 1LL;
            }
        printf("%lld\n", n * ans);  
    }

    return 0;
}

明敕星驰封宝剑,辞君一夜取楼兰。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值