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;
}
posted @ 2021-09-05 11:06  __Anchor  阅读(59)  评论(0编辑  收藏  举报