LOJ6053 简单的函数(min_25筛)

题目链接:LOJ

题目大意:从前有个积性函数 $f$ 满足 $f(1)=1,f(p^k)=p\oplus k$。(异或)求其前 $n$ 项的和对 $10^9+7$ 取模的值。

$1\le n\le 10^{10}$。


这种奇怪但是简洁的积性函数求和,首选 min_25 筛。

首先可以发现,对于质数 $p$,$p\ge 3$ 时 $f(p)=p-1$,$p=2$ 时 $f(p)=p+1$。

所以可以先把 $f(2)$ 看做 $1$,这样方便处理 $g$,最后计算 $S$ 时再加个 $2$ 就好了。

至于 min_25 筛的具体流程,打个广告

时间复杂度 $O(\frac{n^{3/4}}{\log n})$。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=666666,mod=1000000007,inv2=500000004;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
int sq,pri[maxn],pl,tot,id1[maxn],id2[maxn],g0[maxn],g1[maxn],s1[maxn];
bool vis[maxn];
ll n,w[maxn];
inline int add(int a,int b){return a+b<mod?a+b:a+b-mod;}
inline int sub(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int id(ll x){return x<=sq?id1[x]:id2[n/x];}
inline int pre1(ll x){return add(sub(g1[id(x)],g0[id(x)]),x>=2?2:0);}
inline int pre2(ll x){return add(sub(s1[x],x),x?2:0);}
void init(){
    sq=sqrt(n);
    FOR(i,2,sq){
        if(!vis[i]) pri[++pl]=i,s1[pl]=add(s1[pl-1],i);
        FOR(j,1,pl){
            if(i*pri[j]>sq) break;
            vis[i*pri[j]]=true;
            if(i%pri[j]==0) break;
        }
    }
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        w[++tot]=n/l;
        if(n/l<=sq) id1[n/l]=tot;
        else id2[n/(n/l)]=tot;
        int x=w[tot]%mod;
        g0[tot]=sub(x,1);
        g1[tot]=sub(mul(mul(x,x+1),inv2),1);
    }
}
void calc_g(){
    FOR(i,1,pl) FOR(j,1,tot){
        if((ll)pri[i]*pri[i]>w[j]) break;
        g0[j]=sub(g0[j],sub(g0[id(w[j]/pri[i])],i-1));
        g1[j]=sub(g1[j],mul(pri[i],sub(g1[id(w[j]/pri[i])],s1[i-1])));
    }
}
int solve(ll nn,int x){
    if(pri[x]>=nn) return 0;
    int ans=sub(pre1(nn),pre2(x));
    FOR(i,x+1,pl){
        if((ll)pri[i]*pri[i]>nn) break;
        ll pro=1;
        FOR(j,1,50){
            pro*=pri[i];
            if(pro>nn) break;
            ans=add(ans,mul(pri[i]^j,add(j!=1,solve(nn/pro,i))));
        }
    }
    return ans;
}
int main(){
    scanf("%lld",&n);
    init();
    calc_g();
    printf("%d\n",add(1,solve(n,0)));
}
View Code

 

posted @ 2019-05-01 18:07  ATS_nantf  阅读(211)  评论(0编辑  收藏  举报