求欧拉函数的前缀和

如题
输入一个整数 n ( n &lt; = 1 0 10 ) n(n&lt;=10^{10}) n(n<=1010)
输出一个对 1000000007 1000000007 1000000007去模的数

用了ees,有bug,先放一下,等等再调

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define maxx 100105
#define mod 1000000007
using namespace std;
int prime[maxx],cnt;
int phi[maxx];
int sum[maxx];
void init()
{
    for(int i=1;i<maxx;i++)phi[i]=i;
    for(int i=2;i<maxx;i++)sum[i]=i;
    for(int i=2;i<maxx;i++)
    {
        if(phi[i]==i)
        {
            phi[i]--;
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt;j++)
        {
            if((ll)prime[j]*i>=maxx)break;
            sum[i%prime[j]]=0;
            if(i%prime[j])
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            else
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
        }
    }
    for(int i=2;i<maxx;i++)
    {
        phi[i]+=phi[i-1];
        if(phi[i]>=mod)phi[i]-=mod;
        sum[i]+=sum[i-1];
        if(sum[i]>=mod)sum[i]-=mod;
    }
}
ll w[maxx<<1],tot;
int id1[maxx],id2[maxx];
int g1[maxx<<1];//个数和
int g2[maxx<<1];//质数和
int _s;
ll N;
void work()
{
    _s=sqrt(N)+1;
    tot=0;
    for(ll i=1,last;i<=N;i=last+1)
    {
        ll now=N/i;
        last=N/now;
        w[++tot]=now;
        if(now<=_s)id1[now]=tot;
        else id2[N/now]=tot;
        g1[tot]=(now-1)%mod;g2[tot]=(now+2)*(now-1)/2%mod;
    }
    for(int i=1;i<=cnt&&prime[i]<_s;i++)
    {
        ll P=(ll)prime[i]*prime[i];
        for(int j=1;j<=tot&&P<=w[j];j++)
        {
            ll now=w[j]/prime[i];
            int k=now<=_s?id1[now]:id2[N/now];

            g1[j]-=(g1[k]-(i-1)+mod)%mod;
            if(g1[j]<0)g1[j]+=mod;

            int temp=g2[k]-sum[prime[j]-1];
            if(temp<0)temp+=mod;
            g2[j]-=(ll)temp*prime[j]%mod;
            if(g2[j]<0)g2[j]+=mod;
        }
    }
}
int get(ll x)
{
    if(x<=_s)
    {
        int temp=g2[id1[x]]-g1[id1[x]];
        if(temp<0)temp+=mod;
        return temp;
    }
    else
    {
        int temp=g2[id2[N/x]]-g1[id2[N/x]];
        if(temp<0)temp+=mod;
        return temp;
    }
}
int ans;
void dfs(ll num,int res,int cur)
{
    ll temp=num*prime[cur];
    if(temp>N)return;
    ll _res=(ll)res*(prime[cur]-1)%mod;
    ll _n=N/prime[cur];
    if(prime[cur]<_n)
    {
        ll temp1=get(_n)-(sum[prime[cur]]-cur+mod)%mod;
        if(temp1<0)temp1+=mod;
        ans+=_res*temp1%mod;
        if(ans>=0)ans-=mod;
        dfs(temp,_res,cur+1);
    }
    for(;;)
    {
        temp*=prime[cur];
        if(temp>N)break;
        _res*=prime[cur];
        ans+=_res;
        if(ans>=mod)ans-=mod;
        dfs(temp,_res,cur+1);
    }
}
int solve(ll n)
{
    ans=1;
    N=n;
    work();
    ans+=(g2[1]-g1[1]+mod)%mod;
    if(ans>=mod)ans-=mod;
    dfs(1,1,1);
    return ans;
}
int main()
{
    init();
    ll n;cin>>n;
    cout<<solve(n)<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值