#【BZOJ4805】歐拉函數求和(杜教篩) ##題面 BZOJ ##題解 很久沒寫過了 正好看見了順手切一下 令$$S(n)=\sum_{i=1}^n\varphi(i)$$ 設存在的某個積性函數$g(x)$ $$(g*\varphi)(i)=\sum_{d|i}g(d)\varphi(\frac{i}{d})$$ $$\sum_{i=1}^n(g*\varphi(i))(i)$$ $$=\sum_{i=1}^n\sum_{d|i}g(d)\varphi(\frac{i}{d})$$ $$=\sum_{d=1}^ng(d)\sum_{d|i}\varphi(\frac{i}{d})$$ $$=\sum_{d=1}^ng(d)\sum_{i=1}^{n/i}\varphi(i)$$ $$=\sum_{d=1}^ng(d)S(\frac{n}{d})$$php
拿出杜教篩的套路柿子 $$g(1)S(n)=\sum_{i=1}^n(g*\varphi)(i)-\sum_{i=2}^ng(i)S(\frac{n}{i})$$ios
咱們知道$(\varphi*1)=x$ $$S(n)=\sum_{i=1}^ni-\sum_{i=2}^nS(\frac{n}{i})$$ $$S(n)=\frac{n(n+1)}{2}-\sum_{i=2}^nS(\frac{n}{i})$$函數
預處理$10^7$而後杜教篩美滋滋spa
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define ll long long #define RG register #define MAX 10000000 inline int read() { RG int x=0,t=1;RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int pri[MAX+10],tot; bool zs[MAX+10]; ll phi[MAX+10]; void pre() { zs[1]=true;phi[1]=1; for(int i=2;i<=MAX;++i) { if(!zs[i])pri[++tot]=i,phi[i]=i-1; for(int j=1;j<=tot&&pri[j]*i<=MAX;++j) { zs[i*pri[j]]=true; if(i%pri[j])phi[i*pri[j]]=phi[i]*phi[pri[j]]; else{phi[i*pri[j]]=phi[i]*pri[j];break;} } } for(int i=1;i<=MAX;++i)phi[i]+=phi[i-1]; } map<ll,ll> M; ll Solve(ll x) { if(x<=MAX)return phi[x]; if(M[x])return M[x]; ll ret=0; for(ll i=2,j;i<=x;i=j+1) { j=x/(x/i); ret+=(j-i+1)*Solve(x/i); } return M[x]=x*(x+1)/2-ret; } int main() { pre(); int n=read(); printf("%lld\n",Solve(n)); return 0; }