[BZOJ2671]Calc

题目大意:
  对于给定的$n(n<2^{31})$,求$\displaystyle\sum_{a=1}^n\sum_{b=1}^n[(a+b)|ab]$。

思路:
  设$d=\gcd(a,b),a=id,b=jd$,则原式=$\displaystyle\sum_{d=1}^n\sum_{i=1}^{\lfloor\frac n d\rfloor}\sum_{j=1}^{\lfloor\frac n d\rfloor}[(i+j)|ijd]$。
  因为$\gcd(i,j)=1$,所以$\gcd(i,i+j)=\gcd(j,i+j)=1$,所以$(i+j)|ijd$只能是$(i+j)|d$,即原式=$\displaystyle\sum_{d=1}^n\sum_{i=1}^{\lfloor\frac n d\rfloor}\sum_{j=1}^{\lfloor\frac n d\rfloor}[(i+j)|d]$。
  设$k=\frac d{(i+j)}$,则$b=jd=jk(i+j)\leq n$,即对于每组$i,j$,符合条件的$k$有$\lfloor\frac n{(i+j)j}\rfloor$个。
  由于$i\geq1,(i+j)j\leq n$,所以$j\leq\sqrt n-1$。令$i<j$,则原式=$\displaystyle\sum_{j=1}^{\sqrt n-1}\sum_{i=1}^{j-1}[\gcd(i,j)=1]\lfloor\frac n{(i+j)j}\rfloor$。
​ $i$可以数论分块枚举,但不是每一个$i$都对答案有贡献,用$i\perp j$表示$\gcd(i,j)=1$考虑计算一段$\displaystyle\sum_{i=l}^r[i\perp j]$。
  显然,$\displaystyle\sum_{i=l}^r[i\perp j]=\displaystyle\sum_{i=1}^r[i\perp j]-\displaystyle\sum_{i=1}^{l-1}[i\perp j]$。而$\displaystyle\sum_{i=1}^n[i\perp j]=\sum_{i=1}^n\sum_{d|\gcd(i,j)}\mu(d)=\sum_{d|j}\lfloor\frac nd\rfloor$。
  枚举$j$的约数即可。

细节:
  注意判断被$0$除,一旦$\lfloor\frac n{(i+j)j}\rfloor=0$就break掉。

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

 


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