3944: Sum
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3471 Solved: 946
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1
2
8
13
30
2333
Sample Output
2 0
22 -2
58 -3
278 -3
1655470 2
HINT
Source
我们考虑令:
[F_n = sum_{d|n}varphi(d)]
那么,有:
[sum_{i=1}^{n}F_i = sum_{i=1}^{n}sum_{d|i}varphi(d) = sum_{d=1}^{n}varphi(d) imes lfloorfrac{n}{d}
floor = sum_{d=1}^{n}sum_{i=1}^{lfloorfrac{n}{d}
floor}varphi(i)]
为什么最后一步可以这么转化呢?我们考虑一个 (i) ,论 (varphi(i)) 对答案的贡献:
在最后一个等式的左边,(varphi(i)) 对答案的贡献为:(lfloorfrac{n}{i} floor),这很显然。
在等式的右边,当 (i imes d le n) 的时候,(varphi(i))才会对答案产生贡献,所以对于每一个 (dlelfloorfrac{n}{i} floor),(varphi(i))都会对答案产生贡献,所以在等式右边,(varphi(i)) 对答案的贡献也为:(lfloorfrac{n}{i} floor)。
于是等式是成立的。
不懂的话,移步这里:莫比乌斯反演与杜教筛 笔记。
那么就有:
[sum_{i=1}^{n}varphi(i) = sum_{i=1}^{n}sum_{d|i}varphi(d) - sum_{d=2}^{n}sum_{i=1}^{lfloorfrac{n}{d}
floor}varphi(i)]
还有:
[sum_{i=1}^{n}sum_{d|i}varphi(d) = sum_{i=1}^{n}i = frac{n imes(n+1)}{2}]
所以:
[sum_{i=1}^{n}varphi(i) = frac{n imes(n+1)}{2} - sum_{d=2}^{n}sum_{i=1}^{lfloorfrac{n}{d}
floor}varphi(i)]
所以算(sum_{i=1}^{n}varphi(i))的时候就可以记忆化搜索啦。
据说,我们把 (N^{frac{2}{3}}) 之内的答案先筛出来,然后再进行记忆化搜索,复杂度就是 (O(N^{frac{2}{3}}))的了。
然后同理,有:
[sum_{i=1}^{n}mu(i) = 1 - sum_{d=2}^{n}sum_{i=1}^{lfloorfrac{n}{d}
floor}mu(i)]
时空复杂度均为 (O(N^{frac{2}{3}})) 。
#include<cstdio> #include<cstring> #define clr(s) memset(s,0,sizeof s) using namespace std; typedef long long ll; const int N=1e5+5,M=2e6+5; int T,n,m,tot,prime[M/3];bool check[M]; ll phi[M],mu[M]; ll alpha[N],beta[N];bool vis[N]; inline void sieve(){ m=M-5;mu[1]=phi[1]=1; for(int i=2;i<=m;i++){ if(!check[i]) prime[++tot]=i,mu[i]=-1,phi[i]=i-1; for(int j=1;j<=tot&&i*prime[j]<=m;j++){ check[i*prime[j]]=1; if(!(i%prime[j])){mu[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]*(prime[j]-1); } } for(int i=1;i<=m;i++) mu[i]+=mu[i-1],phi[i]+=phi[i-1]; } inline ll GetPhi(int x){ return x<=m?phi[x]:alpha[n/x]; } inline ll GetMu(int x){ return x<=m?mu[x]:beta[n/x]; } void solve(int x){ if(x<=m) return ; int t=n/x; if(vis[t]) return ; vis[t]=1; alpha[t]=(ll)x*((ll)x+1)>>1; beta[t]=1; for(ll i=2,pos;i<=x;i=pos+1){//假如 x=2^31-1,那么i会爆int pos=x/(x/i); solve(x/i); alpha[t]-=GetPhi(x/i)*(pos-i+1); beta[t]-=GetMu(x/i)*(pos-i+1); } } int main(){ sieve(); for(scanf("%d",&T);T--;clr(vis)){ scanf("%d",&n); if(n<=m){ printf("%lld %lld ",phi[n],mu[n]); } else{ solve(n); printf("%lld %lld ",alpha[1],beta[1]); } } return 0; }