達達正在破解一段密碼,他需要回答很多類似的問題:
對於給定的整數a,b和d,有多少正整數對x,y,滿足x<=a,y<=b,並且gcd(x,y)=d。
作爲達達的同學,達達希望得到你的幫助。
輸入格式
第一行包含一個正整數n,表示一共有n組詢問。
接下來n行,每行表示一個詢問,每行三個正整數,分別爲a,b,d。
輸出格式
對於每組詢問,輸出一個正整數,表示滿足條件的整數對數。
數據範圍
1≤n≤50000,
1≤d≤a,b≤50000
輸入樣例:
2
4 5 2
6 4 3
輸出樣例:
3
2
提示:gcd(x,y)返回x,y的最大公約數。
思路:
首先將a除以k,b除以k,題目就轉換爲了求x ≤ a/k, y ≤ b/k,且x,y互質的二元組數了。
定義代表$ x ≤ a, y ≤ bgcd(a,b) $是 i 倍數的二元組(a,b)個數。
易得
定義F[a,b]爲代表且 x,y互質的數個數。
由容斥原理得到
具體過程爲,加上 i = 1時候的情況,減去gcd(x,y)爲 2, 3, 5, 7…倍數時候的情況,再加上gcd(x,y)即是2倍數,也是3倍數時候的情況。。。
於是µ(i)恰好爲莫比烏斯函數
又由除法分塊的知識可以得到: 假設 ,那麼的最大,於是可以連續一段中 [a/x]相等,且[b/x]相等的右端點。
於是分塊的循環,再乘上這一段的莫比烏斯函數前綴和即可。
upd: 又去看了一下網上的題解,發現本題還有莫比烏斯反演的解法,我們在從D函數到F函數的過程,用的是容斥原理,但事實上這個過程可以直接用莫比烏斯反演得到。
初步瞭解了一下莫比烏斯反演,就是這樣一個過程:
如果有(圖自OI WIKI):
那麼可以反演得到:
其中函數代表莫比烏斯函數。
而本題中的D函數其實就代表了上圖中的F函數,F函數就代表了上圖中的g函數。
推導過程爲:
D(n)代表n爲gcd(a,b)約數的二元組數。
F(d)代表d爲gcd(a,b)的二元組數。
反演得到:
這個過程再數學分塊求解就好了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 50005;
int miu[maxn],v[maxn];
void prime() {
for(int i = 1;i < maxn;i++) miu[i] = 1,v[i] = 0;
for(int i = 2;i < maxn;i++) {
if(v[i])continue;
miu[i] = -1;
for(int j = 2 * i;j < maxn;j += i) {
v[j] = 1;
if((j / i) % i == 0) miu[j] = 0;
else miu[j] *= -1;
}
}
for(int i = 1;i < maxn;i++) miu[i] += miu[i - 1];
}
void solve() {
int a,b,k;scanf("%d%d%d",&a,&b,&k);
int ans = 0;
a /= k;b /= k;
if(a > b) swap(a,b);
for(int i = 1,j;i <= a;i = j + 1) { //除法分塊
j = min(a / (a / i),b / (b / i));
ans += (miu[j] - miu[i - 1]) * (a / i) * (b / i);
}
printf("%d\n",ans);
}
int main() {
prime();
int T;scanf("%d",&T);
while(T--) {
solve();
}
return 0;
}