【luogu3768】简单的数学题【杜教筛】【欧拉函数】

题目传送门
题意
(ni=1nj=1ijgcd(i,j)) mod p ( ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j ) )   m o d   p n<=1010 n <= 10 10 p p 为质数。
题解
之前一直想着用μ函数搞,结果死都只能搞到 O(n) O ( n ) 。后来,我fa♂现了bzoj3518点组计数,这道题用了欧拉函数的一个性质 n=i|nφ(i) n = ∑ i | n φ ( i ) ,好像叫做欧拉反演?
于是新日暮里的大门打开了。
于是开始推式子。
ni=1nj=1ijgcd(i,j) ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j )
=ni=1nj=1ijk|gcd(i,j)φ(k) = ∑ i = 1 n ∑ j = 1 n i j ∑ k | g c d ( i , j ) φ ( k )
=nk=1k2φ(k)nki=1nkj=1ij = ∑ k = 1 n k 2 φ ( k ) ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ n k ⌋ i j
我们令 S(n)=ni=1i=n(n+1)2 S ( n ) = ∑ i = 1 n i = n ( n + 1 ) 2
则原式
=nk=1k2φ(k)S(nk)2 = ∑ k = 1 n k 2 φ ( k ) S ( ⌊ n k ⌋ ) 2
于是我们只需要能够快速求出 nk=1k2φ(k) ∑ k = 1 n k 2 φ ( k ) 就可以分块求和计算了。
这不就是杜教筛干的活吗?
ni=1i2j|iφ(j) ∑ i = 1 n i 2 ∑ j | i φ ( j )
=ni=1i3 = ∑ i = 1 n i 3
=S(n)2 = S ( n ) 2
=nj=1nji=1i2j2φ(j) = ∑ j = 1 n ∑ i = 1 ⌊ n j ⌋ i 2 j 2 φ ( j )
因为 inj i ≤ n j
所以 jni j ≤ n i
因此原式
=ni=1i2nij=1j2φ(j) = ∑ i = 1 n i 2 ∑ j = 1 ⌊ n i ⌋ j 2 φ ( j )
i=1 i = 1 带人
ni=1i2φ(i)=S(n)2ni=2i2nij=1j2φ(j) ∑ i = 1 n i 2 φ ( i ) = S ( n ) 2 − ∑ i = 2 n i 2 ∑ j = 1 ⌊ n i ⌋ j 2 φ ( j )
然后就递归计算即可。细节详见代码。
这道题就解决了!
代码

#include<cstdio>
typedef long long ll;
const ll N=5000005,M=1000017;
ll mod,n,ans,p[N],phi[N];
bool vis[N];
struct hashmap{
    int cnt,head[M],nxt[M*10];
    ll to[M*10],v[M*10];
    bool count(ll f){
        ll x=f%M;
        for(int i=head[x];i;i=nxt[i]){
            if(to[i]==f){
                return true;
            }
        }
        return false;
    }
    ll get(ll f){
        ll x=f%M;
        for(int i=head[x];i;i=nxt[i]){
            if(to[i]==f){
                return v[i];
            }
        }
        return 0;
    }
    void set(ll f,ll val){
        ll x=f%M;
        to[++cnt]=f;
        nxt[cnt]=head[x];
        v[cnt]=val;
        head[x]=cnt;
    }
}mp;
inline ll sqr(ll x){
    x%=mod;
    return x*x%mod;
}
ll calc(ll x){
    if(!x){
        return 0;
    }
    x%=mod;
    return sqr((1+x)*x/2)%mod;
}
ll get(ll n){
    n%=mod;
    if(n%3!=1){
        return n*(n+1)/6%mod*(2*n+1)%mod;
    }else if(n&1){
        return (n+1)*(2*n+1)/6%mod*n%mod;
    }else{
        return n*(2*n+1)/6%mod*(n+1)%mod;
    }
}
ll solve(ll n){
    if(n<=5000000){
        return phi[n];
    }
    if(mp.count(n)){
        return mp.get(n);
    }
    ll res=calc(n);
    for(ll i=2,last;i<=n;i=last+1){
        last=n/(n/i);
        res-=solve(n/i)*(get(last)-get(i-1))%mod;
        res%=mod;
    }
    mp.set(n,res);
    return res;
}
int main(){
    scanf("%lld%lld",&mod,&n);
    phi[1]=1;
    for(int i=2;i<=5000000;i++){
        if(!vis[i]){
            p[++p[0]]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=p[0]&&i*p[j]<=5000000;j++){
            vis[i*p[j]]=true;
            if(i%p[j]){
                phi[i*p[j]]=phi[i]*(p[j]-1);
            }else{
                phi[i*p[j]]=phi[i]*p[j];
            }
        }
    }
    for(int i=1;i<=5000000;i++){
        phi[i]=phi[i]*i%mod*i%mod;
        phi[i]+=phi[i-1];
        phi[i]%=mod;
    }
    for(ll i=1,last;i<=n;i=last+1){
        last=n/(n/i);
        ans+=(solve(last)-solve(i-1))*calc(n/i)%mod;
        ans%=mod;
    }
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}

最后附上蒟蒻博主龙hou飞yan凤wu舞chi的公式草稿一张2333 鼠标写字真是太方便啦
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值