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))); }