51nod-1227-平均最小公倍数

题意

定义 \(n\) 的平均最小公倍数:
\[ A(n)=\frac{1}{n}\sum _{i=1}^n\text{lcm}(n,i) \]

\[ \sum _{i=L}^RA(i) \]
\(n\le 10^9\)

分析

有趣的题,学到了一些东西。

我最开始不知道怎么都枚举gcd的时候是整除枚举,然后怎么都做不了。改求和指标为gcd的时候,直接从 1 到 \(n\) 枚举不是很正常的做法吗?于是就开始推………经过很久,答案变成了这个样子:
\[ \sum _{i=1}^ng(i)f(\lfloor\frac{n}{i}\rfloor) \\ g(n)=\sum _{e|n}e\mu (e) \\ f(n)=\frac{n(n+1)(n+2)}{6} \]
由于取底全部都是从 \(n\) 出发的,而且根据底合并的性质,可以用杜教筛来快速计算 \(g\) 的前缀和,分段计算,复杂度为 \(O(n^\frac{3}{4})\)

然后TLE了。

网上有一篇题解说可以直接从 \(L\)\(R\) 计算,不需要算两次,我不是很懂怎么做;用杜教筛只能从前缀和出发吧 。

肯定是做法有点问题。于是去找题解。这题非常简单嘛!不用像我那样。

枚举一对互质的数,再取它们的倍数,比值是定值! 非常妙的方法啊!!!
\[ \begin{aligned} \sum _{i=1}^n\sum _{j=1}^i\frac{j}{\gcd(i,j)}&=\sum _{i=1}^n\sum _{j=1}^i[\gcd(i,j)=1]\sum _{k=1}^{\lfloor\frac{n}{i}\rfloor}j \\ &=\sum _{i=1}^n\lfloor\frac{n}{i}\rfloor\sum _{j=1}^i[\gcd(i,j)=1]j \end{aligned} \]
右边的那个东西……不就是与 \(i\) 互质的数的和!由对称性,可以得到:
\[ \sum _{i=1}^n[\gcd(i,n)=1]i=\frac{\varphi(n)*n+[n=1]}{2} \]
于是就可以简单地用杜教筛求 \(\varphi(n)*n+[n==1]\) 的前缀和,分段计算啦!通过预处理,复杂度为 \(O(n^\frac{2}{3})\)

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
using namespace std::tr1;
typedef long long giant;
const int q=1e9+7;
inline int Plus(int x,int y) {return ((giant)x+(giant)y)%q;}
inline int Sub(int x,int y) {return Plus(x,q-y);}
inline int Multi(int x,int y) {return (giant)x*y%q;}
inline int mi(int x,int y) {
    int ret=1;
    for (;y;y>>=1,x=Multi(x,x)) if (y&1) ret=Multi(ret,x);
    return ret;
}
inline int inv(int x) {return mi(x,q-2);}
const int maxn=2e6+1;
const int i2=inv(2);
const int i6=inv(6);
bool np[maxn];
int p[maxn],ps=0,phi[maxn];
typedef unordered_map<int,int> Map;
Map PHI;
inline int sum(int n) {return Multi(Multi(n,n+1),i2);}
inline int f(int n) {return Multi(Multi(n,n+1),Multi((2ll*n+1)%q,i6));}
int g(int n) {
    if (n<maxn) return phi[n];
    Map::iterator it=PHI.find(n);
    if (it!=PHI.end()) return it->second;
    int &ret=PHI[n]=0;
    for (int i=2,j;i<=n;i=j+1) {
        j=n/(n/i);
        ret=Plus(ret,Multi(Sub(sum(j),sum(i-1)),g(n/i)));
    }
    return ret=Sub(f(n),ret);
}
int calc(int n) {
    int ret=0,last=0;
    for (int i=1,j;i<=n;i=j+1) {
        j=n/(n/i);
        int x=Plus(g(j),1);
        ret=Plus(ret,Multi(n/i,Sub(x,last)));
        last=x;
    }
    return Multi(ret,i2);
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    phi[1]=1;
    for (int i=2;i<maxn;++i) {
        if (!np[i]) p[++ps]=i,phi[i]=i-1;
        for (int j=1,tmp;j<=ps && (tmp=i*p[j])<maxn;++j) {
            np[tmp]=true;
            if (i%p[j]==0) {
                phi[tmp]=Multi(phi[i],p[j]);
                break;
            }
            phi[tmp]=Multi(phi[i],phi[p[j]]);
        }
    }
    for (int i=2;i<maxn;++i) phi[i]=Plus(phi[i-1],Multi(phi[i],i));
    int L,R;
    cin>>L>>R;
    cout<<Sub(calc(R),calc(L-1))<<endl;
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/7397687.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值