题解:疯狂lcm

%你赛打到一半来写个题解

link:疯狂lcm

题意,求:

i=1nlcm(i,n)

不多说废话,直接开推:

=ni=1nigcd(i,n)=ndni=1nid[gcd(i,n)=d]=ndni=1nid[gcd(id,nd)=1]=ndni=1ndi[gcd(i,nd)=1]

我们发现这个 nd 实际上还是枚举的 d,那我们来直接替换:

ndni=1di[gcd(i,d)=1]

那么后边这个 i=1di[gcd(i,d)=1] 实际上就是小于 d 且与 d 互质的数的和,这玩意就等于 φ(n)×n2,证明如下:

由于更相减损法 gcd(n,x)=gcd(n,nx) 可知,所有与 n 互质的数都是成对出现的,每一对的和为 n,一共有 φ(n)2 对,那么和为 φ(n)×n2,当然要在 n>1 的条件下满足。

那么原式即变为:

ndnφ(d)×d2

但是如果 d=1 上式变为 0,就不成立了,因此 d=1 的时候特判一下就好。

容易发现这是一个 Θ(n) 的算法,优化一下,设 f(n)=dnφ(d)×d,也就是 1(φ(n)×n),它显然是个积性函数,那可以考虑用个筛子筛一下,很容易将复杂度降到 logn 级别。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int n,T;
int prim[N],phi[N],f[N];
void init(){
    phi[1]=1;
    for(int i=2;i<=N;++i){
        if(!phi[i]) prim[++prim[0]]=i,phi[i]=i-1;
        for(int j=1;j<=prim[0]&&i*prim[j]<=N;++j){
            if(!(i%prim[j])){
                phi[i*prim[j]]=prim[j]*phi[i];
                break;
            }else phi[i*prim[j]]=(prim[j]-1)*phi[i];
        }
    }
    for(int i=1;i<=N;++i)
        for(int j=i;j<=N;j+=i) f[j]+=(i==1)?1:(phi[i]*i/2);
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    init();
    cin>>T;
    while(T-->0){
        cin>>n;
        cout<<f[n]*n<<'\n';
    }
    return 0;  
}
posted @   Melting_Pot  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示