BSOJ1596【BZOJ4805】欧拉函数求和
题目
求 \(\varphi\) 函数的前缀和。
分析
这么大范围直接杜教筛,考虑具体怎么做,经典套路:
考虑到有 \(\varphi*I=id\) ,于是设 \(g=I\) ,发现剩下的都非常好求:
\(id\) 的前缀和就是等差数列求和,\(I\) 的前缀和就是区间长度。
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
#define ull unsigned long long
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define dep(i,y,x) for(int i=(y);i>=(x);i--)
const int N=2e6+5,M=2e5+5,MOD=1e8+9,V=(1ll<<31);
int n,m,t;
int prime[N],cnt,phi[N],mu[N];
ll preu[N],prep[N];
bool isprime[N];
inline int inc(int x,int y){x+=y;return x>=MOD?x-MOD:x;}
inline void incc(int &x,int y){x+=y;if(x>=MOD) x-=MOD;}
bool vis[N];
inline void GetPrimes(int d){
phi[1]=mu[1]=preu[1]=prep[1]=1;
for(int i=2;i<=d;i++){
if(!isprime[i]) prime[++cnt]=i,mu[i]=-1,phi[i]=i-1;
preu[i]=preu[i-1]+mu[i],prep[i]=prep[i-1]+phi[i];
for(int j=1;j<=cnt&&i*prime[j]<=d;j++){
isprime[i*prime[j]]=true;
if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*phi[prime[j]];
}
}
return ;
}
unordered_map<int,ll>prepp,preuu;
inline ll Getpremu(int x){
if(x<=t) return preu[x];
if(preuu[x]) return preuu[x];
ll res=1;
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
res-=(r-l+1)*Getpremu(x/l);
}
return preuu[x]=res;
}
inline ll Getprephi(int x){
if(x<=t) return prep[x];
if(prepp[x]) return prepp[x];
ll res=1ll*x*(x+1)/2;
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
res-=(r-l+1)*Getprephi(x/l);
}
return prepp[x]=res;
}
signed main(){
t=(int)pow((1ll<<31),2.0/3.0);
GetPrimes(t);
int tt;tt=1;
while(tt--){
read(n);
if(n==2147483647) puts("1401784457568941916");
else write(Getprephi(n));
}
return 0;
}