洛谷P3455 [POI2007]ZAP-Queries(莫比乌斯反演)

传送门

设$$f(k)=\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)=k]$$

$$g(n)=\sum_{n|k}f(k)=\lfloor\frac{a}{n}\rfloor\lfloor\frac{b}{n}\rfloor$$

根据莫比乌斯反演定理可以推出$$f(n)=\sum_{n|k}\mu(\lfloor\frac{k}{n}\rfloor)g(k)$$

那么可以发现$ans=f(d)$

然后用推出来的结论带进去

$$ans=\sum_{d|k}\mu(\lfloor\frac{k}{d}\rfloor)g(k)$$

枚举$\lfloor\frac{k}{d}\rfloor$设为$t$

$$ans=\sum_{t=1}^{min(\lfloor\frac{a}{d}\rfloor,\lfloor\frac{b}{d}\rfloor)}\mu(t)\lfloor\frac{a}{td}\rfloor\lfloor\frac{b}{td}\rfloor$$

对于$$\lfloor\frac{a}{td}\rfloor\lfloor\frac{b}{td}\rfloor$$相同的一段我们可以直接用前缀和算出答案

总而言之就是先预处理出$\mu$的前缀和然后用整除分块,那么每一次询问的复杂度就是$O(\sqrt{n})$

 1 //minamoto
 2 #include<cstdio>
 3 #include<iostream>
 4 #define ll long long
 5 using namespace std;
 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 7 char buf[1<<21],*p1=buf,*p2=buf;
 8 inline int read(){
 9     #define num ch-'0'
10     char ch;bool flag=0;int res;
11     while(!isdigit(ch=getc()))
12     (ch=='-')&&(flag=true);
13     for(res=num;isdigit(ch=getc());res=res*10+num);
14     (flag)&&(res=-res);
15     #undef num
16     return res;
17 }
18 const int N=6e5+5;
19 int p[N],mu[N],vis[N],m,sum[N];ll ans,lim;
20 void init(int n){
21     mu[1]=1;
22     for(int i=2;i<=n;++i){
23         if(!vis[i]) mu[i]=-1,p[++m]=i;
24         for(int j=1;j<=m&&p[j]*i<=n;++j){
25             vis[i*p[j]]=1;
26             if(i%p[j]==0) break;
27             mu[i*p[j]]=-mu[i];
28         }
29     }
30     for(int i=1;i<=n;++i) sum[i]=sum[i-1]+mu[i];
31 }
32 int main(){
33 //    freopen("testdata.in","r",stdin);
34     int n,m,T,d;scanf("%d",&T);
35     init(50000);
36     while(T--){
37         scanf("%d%d%d",&n,&m,&d);ans=0;
38         lim=min(n/d,m/d);
39         
40         for(int l=1,r;l<=lim;l=r+1){
41             r=min(n/(n/l),m/(m/l));
42             ans+=1ll*(n/(l*d))*(m/(l*d))*(sum[r]-sum[l-1]);
43         }
44         printf("%lld\n",ans);
45     }
46     return 0;
47 }

猜你喜欢

转载自www.cnblogs.com/bztMinamoto/p/9688653.html