[BZOJ3944]Sum

题目大意:
  对于给定的$n(n<2^{31})$,求$\sum_{i=1}^n\varphi(i)$和$\sum_{i=1}^n\mu(i)$。

思路:
  $\sum_{i=1}^n\varphi(i)$同BZOJ4805。
  设$S(n)=\sum_{i=1}^n\mu(i)$。
  因为$\sum_{d|n}\mu(d)=[n=1]$,$S(n)=\sum_{i=1}^n([i=1]-\sum_{d|i,d<i}\mu(d))=1-\sum_{i=2}^nS(\lfloor\frac ni\rfloor)$。
  线性筛预处理$S$的前$n^{\frac23}$项,剩下的数论分块计算,用哈希表保存已经算过的值,记忆化搜索即可。时间复杂度$O(n^{\frac23})$。

细节:
  两个询问必须写在一个函数里,否则会TLE。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<hash_map>
 4 typedef long long int64;
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int N=1664511,M=125640;
13 bool vis[N];
14 int m,mu[N],phi[N],p[M];
15 int64 s_mu[N],s_phi[N];
16 __gnu_cxx::hash_map<int,std::pair<int64,int64> > map;
17 inline void sieve() {
18     phi[1]=mu[1]=s_phi[1]=s_mu[1]=1;
19     for(register int i=2;i<N;i++) {
20         if(!vis[i]) {
21             p[++p[0]]=i;
22             mu[i]=-1;
23             phi[i]=i-1;
24         }
25         for(register int j=1;j<=p[0]&&i*p[j]<N;j++) {
26             vis[i*p[j]]=true;
27             if(i%p[j]==0) {
28                 mu[i*p[j]]=0;
29                 phi[i*p[j]]=phi[i]*p[j];
30                 break;
31             }
32             mu[i*p[j]]=-mu[i];
33             phi[i*p[j]]=phi[i]*phi[p[j]];
34         }
35         s_mu[i]=s_mu[i-1]+mu[i];
36         s_phi[i]=s_phi[i-1]+phi[i];
37     }
38 }
39 inline std::pair<int64,int64> calc(const int64 &n) {
40     if(n<N) return std::make_pair(s_phi[n],s_mu[n]);
41     if(map.count(n)) return map[n];
42     std::pair<int64,int64> ans=std::make_pair((int64)n*(n+1)/2,1);
43     for(register int64 l=2,r;l<=n;l=r+1) {
44         r=n/(n/l);
45         std::pair<int64,int64> tmp=calc(n/l);
46         ans.first-=tmp.first*(r-l+1);
47         ans.second-=tmp.second*(r-l+1);
48     }
49     return map[n]=ans;
50 }
51 int main() {
52     sieve();
53     for(register int T=getint();T;T--) {
54         std::pair<int64,int64> ans=calc(getint());
55         printf("%lld %lld\n",ans.first,ans.second);
56     }
57     return 0;
58 }

 

posted @ 2018-02-26 16:28  skylee03  阅读(108)  评论(0编辑  收藏  举报