1. 程式人生 > >BZOJ 2301: [HAOI2011]Problem b(莫比烏斯反演)

BZOJ 2301: [HAOI2011]Problem b(莫比烏斯反演)

計算 algo cto ref blog image get txt +=

http://www.lydsy.com/JudgeOnline/problem.php?id=2301

題意:
對於給出的n個詢問,每次求有多少個數對(x,y),滿足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函數為x和y的最大公約數。

思路:

先簡單介紹一下莫比烏斯反演在數論中的作用:

技術分享

那麽怎麽做這道題呢?

技術分享

接下來我們只需要枚舉d就可以了,但是這裏還有一個可以優化的地方,我們依次+1枚舉d的時候,有時候n/d和m/d是不會改變的,比如說現在n=m=,那麽d=3,4,5時n/d和m/d都是不變的,這樣一來的話我們可以分塊處理,需要計算一下莫比烏斯的前綴和,就可以將3,4,5的值一起計算了,這樣一來,枚舉的數量將大大減小。具體看代碼。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<sstream>
 6 #include<vector>
 7 #include<stack>
 8 #include<queue>
 9 #include<cmath>
10 #include<map>
11 #include<set>
12
using namespace std; 13 typedef long long ll; 14 typedef pair<int,int> pll; 15 const int INF = 0x3f3f3f3f; 16 const int maxn = 1e5 + 5; 17 18 int a, b, c, d, k; 19 20 bool check[maxn]; 21 int prime[maxn]; 22 int mu[maxn]; 23 int sum[maxn]; 24 25 void Moblus() 26 { 27 memset(check, false
, sizeof(check)); 28 mu[1] = 1; 29 int tot = 0; 30 for (int i = 2; i <= maxn; i++) 31 { 32 if (!check[i]) 33 { 34 prime[tot++] = i; 35 mu[i] = -1; 36 } 37 for (int j = 0; j < tot; j++) 38 { 39 if (i * prime[j] > maxn) 40 { 41 break; 42 } 43 check[i * prime[j]] = true; 44 if (i % prime[j] == 0) 45 { 46 mu[i * prime[j]] = 0; 47 break; 48 } 49 else 50 { 51 mu[i * prime[j]] = -mu[i]; 52 } 53 } 54 } 55 return ; 56 } 57 58 int solve(int n, int m) 59 { 60 if(n>m) swap(n,m); 61 int ans=0; 62 63 for(int i=1,last=0;i<=n;i=last+1) 64 { 65 last=min(n/(n/i),m/(m/i)); //分塊處理 66 ans+=(sum[last]-sum[i-1])*(n/i)*(m/i); 67 } 68 return ans; 69 } 70 71 72 int main() 73 { 74 //freopen("in.txt","r",stdin); 75 int T; 76 Moblus(); 77 sum[0]=0; 78 for(int i=1;i<=maxn;i++) sum[i]=sum[i-1]+mu[i]; 79 80 scanf("%d",&T); 81 while(T--) 82 { 83 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 84 printf("%d\n",solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k)); 85 } 86 return 0; 87 }

BZOJ 2301: [HAOI2011]Problem b(莫比烏斯反演)