51nod [1220 约数之和]

题意

题意:已知 σ(i)=d|id ,求 i=1nj=1nσ(ij) ,其中 1n109

题目链接

题解

首先我们先来看一个结论: σ(ij)=x|iy|jxy×[gcd(ix,y)=1] 。其中 [x] 表示在 x 成立的时候这个值为 1 ,不成立时这个值为 0

为啥这个结论是对的呢?前面的 xy 实际上是在枚举这个约数的值,但是为什么在 xy 后面还要乘上一个数字呢?

因为如果不乘那个数字会重复计算,例如在 i=6,j=3x=1,y=3x=3,y=1 这两组取值都可以得到一个 ij 的约数 3 ,这时候这个约数就被计算了两遍。加上这个约束就不会重复计算了。

下来我们来推式子。

欢乐的颓柿子时间

i=1nj=1nσ(ij)=i=1nj=1nx|iiy|jjxy×[gcd(ix,y)=1]展开上面的式子=i=1nj=1nx|iiy|jjiyx×[gcd(x,y)=1]这里换了一下枚举的方式=i=1nj=1nx|iiy|jjiyx×ε(gcd(x,y))ε(n)[n=1]等价=i=1nj=1nx|iiy|jjiyx×d|gcd(x,y)μ(d)由迪利克雷卷积知μI=ε=d=1nμ(d)i=1nj=1nx|id|xiy|jd|yjiyx换一下枚举顺序=d=1ndμ(d)i=1ndj=1ndx|iy|jiyxi,j,x,y都是 d 的倍数,我们枚举他们是 d 的几倍=d=1ndμ(d)(i=1ndx|iix)(j=1ndy|jy)换一下枚举顺序=d=1ndμ(d)(i=1ndσ(i))2然后会发现一些神奇的东西

h(n)=(i=1nσ(i))2

那么上面的式子可以变成 d=1ndμ(d)h(nd)

可以发现,如果可以比较快速求出 h(i) 的值以及 dμ(d) 的前缀和就可以对整个式子进行数论分块。

Part1

我们先来看一下 h 的值如何求。

h(n)=(i=1nσ(i))2=(i=1nj|ij)2=(j=1njnj)2

这个玩意也可以数论分块(瞬间窒息

Part2

我们设 f(i)=iμ(i)

那么我们要求的 S(i)=j=1if(j)

既然要在 109 范围内求一个函数(就是 f )的前缀和,并且这还是一个积性函数,我们首先考虑杜教筛。

不难发现 f=idμ ,那我们设 g=id

让我们把杜教筛的式子写出来:

S(n)g(1)=i=1n(fg)(i)i=2nS(ni)g(i)

g 的前缀和挺好求,不说了。

我们来推一下 fg 的。

i=1n(fg)(i)=i=1nd|id×μ(d)×id=i=1nid|iμ(d)=i=1ni×(μI)(i)=i=1ni×ε(i)=1

Game over!

代码

#include<cstdio>
#include<map>
using namespace std;
const int mod=1000000007;
using ll=long long;
inline int read()
{
    int s=0,w=1;char ch;
    while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
int pri[1000001];
bool is[1000001];
ll f[1000001];
map<int,ll>mf;
ll ans;
int n;
ll run(int l,int r)
{
    return (ll)(r-l+1)*(l+r)/2%mod;
}
ll get(int num)
{
    if(num<=1000000) return f[num];
    if(mf.count(num)) return mf[num];
    // printf("get %d\n",num);
    ll val=1;
    for(int l=2,r;l<=num;l=r+1)
    {
        r=num/(num/l);
        val=(val-run(l,r)*get(num/l))%mod;
    }
    return mf[num]=(val+mod)%mod;
}
ll getpre(int num)
{
    ll ans=0;
    for(int l=1,r;l<=num;l=r+1)
    {
        r=num/(num/l);
        ans=(ans+run(l,r)*(num/l))%mod;
    }
    return ans*ans%mod;
}
int main()
{
    n=1000000,f[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!is[i]) pri[++pri[0]]=i,f[i]=-1;
        for(int j=1;j<=pri[0]&&i*pri[j]<=n;j++)
        {
            is[i*pri[j]]=1;
            if(i%pri[j]==0) break;
            f[i*pri[j]]=-f[i];
        }
    }
    for(int i=1;i<=n;i++) f[i]=(f[i]*i+f[i-1]+mod)%mod;
    n=read();
    for(int l=1,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        ans=(ans+(get(r)-get(l-1))*getpre(n/l))%mod;
    }
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}
posted @   Wuyanru  阅读(88)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示