[HAOI2011]Problem b&&[POI2007]Zap

题目大意:
  $q(q\leq50000)$组询问,对于给定的$a,b,c,d(a,b,c,d\leq50000)$,求$\displaystyle\sum_{i=a}^b\sum_{j=c}^d[\gcd(i,j)=k]$。

思路:
  首先可以利用容斥原理,将$(a,b,c,d)$的询问拆成$(b,d)$、$(a-1,d)$、$(b,c-1)$和$(a-1,c-1)$四个询问,对于询问$(n,m)$,有:
$$
\begin{align*}
&\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]\\
&=\sum_{k=1}^{\min(n,m)}\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}[\gcd(i,j)=1]\\
&=\sum_{k=1}^{\min(n,m)}\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\\
&=\sum_{k=1}^{\min(n,m)}\sum_{d=1}^{\min(\lfloor\frac{n}{k}\rfloor,\lfloor\frac{m}{k}\rfloor)}\mu(d)\sum_{1\leq i\leq\lfloor\frac{n}{k}\rfloor且d|i}\sum_{1\leq i\leq\lfloor\frac{m}{k}\rfloor且d|j}1\\
&=\sum_{k=1}^{\min(n,m)}\sum_{d=1}^{\min(\lfloor\frac{n}{k}\rfloor,\lfloor\frac{m}{k}\rfloor)}\mu(d)\lfloor\frac{\lfloor\frac{n}{k}\rfloor}{d}\rfloor\lfloor\frac{\lfloor\frac{m}{k}\rfloor}{d}\rfloor
\end{align*}
$$
  $\mu$的前缀和可以预处理。因此到最后一步时,复杂度已经是$O(n)$的了,然而$q\leq50000$,还是会TLE。考虑某些时候对于某一范围内的$k$,$\lfloor\frac{\lfloor\frac{n}{k}\rfloor}{d}\rfloor$和$\lfloor\frac{\lfloor\frac{m}{k}\rfloor}{d}\rfloor$分别各自相等,因此我们可以将相等的情况一起考虑,这样总共有$O(\sqrt n)$种情况,时间复杂度$O(q\sqrt n)$。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<algorithm>
 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=50001,M=5134;
13 bool vis[N];
14 int mu[N],sum[N],p[M];
15 inline void sieve() {
16     mu[1]=1;
17     for(register int i=2;i<N;i++) {
18         if(!vis[i]) {
19             p[++p[0]]=i;
20             mu[i]=-1;
21         }
22         for(register int j=1;j<=p[0]&&i*p[j]<N;j++) {
23             vis[i*p[j]]=true;
24             if(i%p[j]==0) {
25                 mu[i*p[j]]=0;
26                 break;
27             }
28             mu[i*p[j]]=-mu[i];
29         }
30     }
31     for(register int i=1;i<N;i++) {
32         sum[i]=sum[i-1]+mu[i];
33     }
34 }
35 inline int64 calc(int n,int m,const int &k) {
36     int64 ret=0;
37     n/=k,m/=k;
38     const int lim=std::min(n,m);
39     for(register int i=1,j;i<=lim;i=j+1) {
40         j=std::min(n/(n/i),m/(m/i));
41         ret+=(int64)(sum[j]-sum[i-1])*(n/i)*(m/i);
42     }
43     return ret;
44 }
45 int main() {
46     sieve();
47     for(register int i=getint();i;i--) {
48         const int a=getint(),b=getint(),c=getint(),d=getint(),k=getint();
49         printf("%lld\n",calc(b,d,k)-calc(a-1,d,k)-calc(b,c-1,k)+calc(a-1,c-1,k));
50     }
51     return 0;
52 }

 

posted @ 2018-02-24 08:39  skylee03  阅读(110)  评论(0编辑  收藏  举报