51Nod 1239 欧拉函数之和

来源:互联网 发布:花形透 知乎 编辑:程序博客网 时间:2024/06/05 06:41

转载请注明出处,谢谢http://blog.csdn.net/bigtiao097?viewmode=contents

题意

s(n)=i=1nφ(i)

φ(n)是欧拉函数
给定n,求s(n)mod109+7  (2n1010)

思路

这是我学杜教筛做的第二个题,第一个就是和这个非常相似的求莫比乌斯函数之和
给大家推荐唐老师的一篇文章,个人感觉讲的非常好,我就是在这里学的
欧拉函数的一个性质是这样的

n=d|nφ(d)

所以就有
n×(n+1)2=i=1ni=i=1nd|iφ(d)=i=1nd=1niφ(d)=i=1ns(ni)(1)(2)

这样就得到了
s(n)=n×(n+1)2i=2ns(ni)

感觉上面式子中最难理解的地方就是(1)(2)的过程了,这里稍微解释一下:
可以这样理解

i=1nd|iφ(d)=i=1nd|iφ(id)=i=1nj=1niφ(j)

第二个式子中的i 理解为枚举每一个数,最后一个式子中的i 理解为枚举每一个因子

对于

s(n)=n×(n+1)2i=2ns(ni)

然后就可以递归求解了,顺便加记忆化,顺便加上分段优化来求了
关于复杂度的问题,具体可以看上面推荐的文章,这里稍微说一下

  • 如果我们先提前筛选出了n23φ(n)的前缀和,总体复杂度大概是O(n23)
  • 如果没有提前处理一下,直接递归求解的话, 总体复杂度大概是O(n34)

然后再说一下递归过程中记忆化的时候用的什么数据结构
可以看一下我的另一篇文章里面说的传送门
这个题用三种结构都能过,而且时间差不多


具体代码如下:
Result:Accepted    Memory:26108 KB    Time :1406 ms

#include<bits/stdc++.h>const int maxn = 4641590;//maxn = n^(2/3)const int mod = 1e9+7;using namespace std;typedef long long ll;const int HASH_MOD=987654;int phi[maxn];bool vis[maxn];int p[maxn];ll n;unordered_map<ll,ll> mp;void euler_init(){    phi[1]=1;    for(int i=2;i<maxn;i++)    {        if(!vis[i])        {            phi[i]=i-1;            p[++p[0]]=i;        }        for(int j=1;p[j]*i<maxn;j++)        {            vis[p[j]*i]=1;            if(i%p[j]==0)            {                phi[p[j]*i]=phi[i]*p[j]%mod;                break;            }            phi[p[j]*i]=phi[i]*(p[j]-1)%mod;        }    }    for(int i=1;i<maxn;i++)        phi[i]= (phi[i]+phi[i-1])%mod;}ll mod_mul(ll x,ll n){    ll res = 0;    while(n>0)    {        if(n&1) res = (res+x)%mod;        x = (x<<1)%mod;        n>>=1;    }    return res;}ll calc(ll x){    unordered_map<ll,ll>::iterator it;    if (x<maxn) return phi[x];    if((it=mp.find(x))!= mp.end())        return it->second;    ll ans = 0;    if(x&1)        ans = mod_mul(x,(x+1)/2);    else        ans = mod_mul(x/2,x+1);    for(ll l=2,r; l<=x; l=r+1)    {        r=x/(x/l);        ans=(ans-calc(x/l)*((r-l+1)%mod)%mod+mod)%mod;    }    return mp[x] = ans;}int main(){    euler_init();    scanf("%lld",&n);    printf("%lld",calc(n));}
原创粉丝点击