51Nod1220 约数之和

本文针对特定形式的数学求和问题提供了解题思路与详细解答过程。通过分析因子出现频率和利用杜教筛算法,实现了高效求解。

题目

Σni=1Σnj=1σ1(ij)Σi=1nΣj=1nσ1(ij)
其中,σ1(n)=Σd|nd,n109σ1(n)=Σd|nd,n≤109

解题思路

错误的结论:Ans=Σni=1Σnj=1Σa|iΣb|j[gcd(a,b)=1]abAns=Σi=1nΣj=1nΣa|iΣb|j[gcd(a,b)=1]ab
正确的结论:Ans=Σni=1Σnj=1Σa|iΣb|j[gcd(a,b)=1]ajbAns=Σi=1nΣj=1nΣa|iΣb|j[gcd(a,b)=1]ajb
为什么这才是对的?
打表,得出ijij的每个因子出现了一次或多次。
那么这些出现了多次的因子为什么会出现多次呢?
看下面的式子。

a1jb1=a2jb2a1b2=a2b1a1jb1=a2jb2即a1b2=a2b1

不妨设a2>a1a2>a1,则a2=ka1,k>1,kZa2=k∗a1,k>1,k∈Z,推出b2=kb1b2=k∗b1
这相当于将jb1jb1中的因数kk给了a2
原式可以变成
a1jb1=a1jkb1ka1jb1=a1j∗kb1∗k

即这是一个约分的过程。
则等式右边说明k|gcd(a2,b2)k|gcd(a2,b2)
证毕。
大神Drin_E说,先考虑每个a,ba,b对答案的贡献,最后才划开[gcd(a,b)=1][gcd(a,b)=1]部分。
所以,原式变成了Σna=1Σnb=1[gcd(a,b)=1]anaS(nb)Σa=1nΣb=1n[gcd(a,b)=1]a∗⌊na⌋∗S(⌊nb⌋)
考虑每个aa为答案贡献了多少。
相当于固定了一个值,看其余值的变化规律。
当一个a确定的时候,j=b,2b,...,nbbj=b,2b,...,⌊nb⌋∗b。所以要乘上S(nb)S(⌊nb⌋)
对于每一个aa,在原来的i,j两重循环中共出现了,na,⌊na⌋次。
现在再划开式子[gcd(a,b)=1][gcd(a,b)=1]
则原式为:Σna=1anaΣnb=1S(nb)Σx|gcd(a,b)μ(x)Σa=1na∗⌊na⌋Σb=1n∗S(⌊nb⌋)Σx|gcd(a,b)μ(x)
=Σnx=1μ(x)Σnxa=1axnxaΣnxb=1S(nxb)=Σx=1nμ(x)Σa=1⌊nx⌋ax∗⌊⌊nx⌋a⌋Σb=1⌊nx⌋S(⌊⌊nx⌋b⌋)
Σni=1ini=Σni=1S(ni)Σi=1ni∗⌊ni⌋=Σi=1nS(⌊ni⌋)
所以直接杜教筛搞一下就OK了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000010
#define LL long long
#define mo 1000000007
#define inv2 500000004
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
LL i,j,k,l,n,m,ans,ny;
LL mu[N],pri[N];
LL md[N],smi[N];
bool bz[N];
void pre(){
    LL i,j;
    mu[1]=smi[1]=1;
    fo(i,2,N-10){
        if(!bz[i]){
            pri[++pri[0]]=i;
            mu[i]=-1;
        }
        for(j=1;j<=pri[0]&&i*pri[j]<=N-10;j++){
            if(i*pri[j]>N-10)break;
            bz[i*pri[j]]=1;
            if(i%pri[j]==0)break;
            mu[i*pri[j]]=-mu[i];
        }
    }
    fo(i,2,N-10){
        smi[i]=smi[i-1]+i*mu[i];
        smi[i]=smi[i]<0?smi[i]+mo:smi[i];
        smi[i]=smi[i]>0?smi[i]-mo:smi[i];
    }
}
LL Sum(LL l,LL r){
    return ((r-l+1)*(r+l)%mo)*inv2%mo;
}
LL djs(LL x){
    if(x<=N-10)return smi[x];
    if(md[n/x])return md[n/x];
    LL rs=1,i;
    for(i=2;i<=x;i=x/(x/i)+1)
        rs=(rs-Sum(i,x/(x/i))*djs(x/i)%mo+mo)%mo;
    return md[n/x]=rs;
}
LL getS(LL x){
    LL i,rs=0;
    for(i=1;i*i<=x;i++)rs=(rs+i*(x/i))%mo;
    for(;i<=x;i=x/(x/i)+1)
        rs=(rs+Sum(i,x/(x/i))*(x/i)%mo)%mo;
    return (rs*rs)%mo;
}
int main(){
    freopen("1220.in","r",stdin);
    scanf("%lld",&n);
    pre();
    ans=0;
    for(i=1;i<=n;i=n/(n/i)+1){
        k=(djs(n/(n/i))-djs(i-1)+mo)%mo;
        ans=(ans+k*getS(n/i))%mo;
    }
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值