「luogu3455」[POI2007]ZAP-Queries

原问题转化为求有多少个二元组(x,y)满足 x≤a/k,y≤b/k 且 gcd(x,y)=1。

我们获得两个函数f(i)和g(i)。

f(i)表示满足 x≤a,y≤b 且 gcd(x,y)=i 的二元组个数,g(i)表示满足 x≤a,y≤b 且 gcd(x,y) 为 i 的倍数个二元组个数。

发现 g(i)=a/i*b/i,于是可以用莫比乌斯反演求出f(i)。

但是一个一个求g(i)时会超时,考虑优化。

发现g(i)有很多段的值都是一样的,可以求出莫比乌斯函数的前缀和后计算一整段的答案。

也就是数论分块

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=50010;
 4 int n,a,b,k,u[N],prime[N],tot;
 5 bool isp[N];
 6 inline void getu(int k){
 7     u[1]=1;
 8     for(int i=2;i<=k;i++){
 9         if(!isp[i]) prime[++tot]=i,u[i]=-1;
10         for(int j=1;j<=tot&&1LL*prime[j]*i<=k;j++){
11             u[prime[j]*i]=-u[i],isp[prime[j]*i]=1;
12             if(!(i%prime[j])){u[prime[j]*i]=0;break;}
13         }
14     }
15     for(int i=2;i<=k;i++) u[i]+=u[i-1];
16     return;
17 }
18 
19 void solve(){
20     scanf("%d%d%d",&a,&b,&k);
21     a/=k,b/=k;
22     if(a>b) swap(a,b);
23     int ans=0;
24     int now=1,nxt;
25     while(now<=a){
26         nxt=min(a/(a/now),b/(b/now));
27         ans+=(u[nxt]-u[now-1])*(a/now)*(b/now);
28         now=nxt+1;
29     }
30     printf("%d\n",ans);
31     return;
32 }
33 int main(){
34     scanf("%d",&n);
35     getu(50000);
36     while(n--) solve();
37     return 0;
38 }

 

posted @ 2018-03-14 19:19  Cupcake  阅读(125)  评论(0编辑  收藏  举报