1. 程式人生 > >【HDU 1695】GCD(莫比烏斯反演)

【HDU 1695】GCD(莫比烏斯反演)

GCD
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 16412 Accepted Submission(s): 6314

Problem Description
Given 5 integers: a, b, c, d, k, you’re to find x in a…b, y in c…d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you’re only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.

Yoiu can assume that a = c = 1 in all test cases.

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.

Output
For each test case, print the number of choices. Use the format in the example.

Sample Input
2
1 3 1 5 1
1 11014 1 14409 9

Sample Output
Case 1: 9
Case 2: 736427

Hint
For the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).

直接莫比烏斯反演就好了。
i = 1 b j = 1 d [ g c d ( i , j ) = = k ] \sum_{i=1}^{b}\sum_{j=1}^{d}[gcd(i,j)==k]

我們把 k k 除掉,則有:
i = 1 n j = 1 m [ g c d ( i , j ) = = 1 ] \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==1]

其中 n = b / k m = d / k n=b/k,m=d/k

我們令 f ( d ) = i = 1 n j = 1 m [ g c d ( i , j ) = = d ] f(d)=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==d]

F ( d ) = i = 1 n j = 1 m [ d g c d ( i , j ) ] F(d)=\sum_{i=1}^{n}\sum_{j=1}^{m}[d|gcd(i,j)]

F ( d ) = i d f ( i ) F(d)=\sum_{i|d}f(i)

莫比烏斯反演得:
f ( d ) = d i μ ( i / d ) F ( i ) f(d)=\sum_{d|i}\mu(i/d)*F(i)

因此 f ( 1 ) = i = 1 m i n ( n , m ) μ ( i ) F ( i ) f(1)=\sum_{i=1}^{min(n,m)}\mu(i)F(i)

F ( i ) = ( n / i ) ( m / i ) F(i)=\lfloor(n/i)\rfloor*\lfloor(m/i)\rfloor

最終我們得到反演公式為:

i = 1 m i n ( n , m ) μ ( i ) ( n / i ) ( m / i ) \sum_{i=1}^{min(n,m)}\mu(i)*\lfloor(n/i)\rfloor*\lfloor(m/i)\rfloor

但是我們求的和題意不太一樣,因為題目規定 ( i , j ) = = ( j , i ) (i,j)==(j,i) ,因此我們計算重複了,那麼我們怎麼去重呢,題目中說 a = c = 1 a=c=1 ,我們只需要考慮
b , d b,d 即可,我們發現重複的部分只可能存在於 [ 1 , m i n ( b , d ) ] [1,min(b,d)] 中,在這一段我們恰好計算了兩次,直接減去就可以了。
最後還要注意(k==0)的情況。不然可能會RE

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
ll prime[maxn+100];
bool vis[maxn+100];
int mbs[maxn+100];
ll sum[maxn+100];
void mobious(){
	mbs[1]=1;
	int cnt=0;
	for(int i=2;i<=maxn;i++){
		if(!vis[i]){
			prime[++cnt]=i;
			mbs[i]=-1;
		}
		for(int j=1;j<=cnt&&i*prime[j]<=maxn;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0){
				mbs[i*prime[j]]=0;
				break;
			}
			else mbs[i*prime[j]]=-mbs[i];
		}
	}
	for(int i=1;i<=maxn;i++){
		sum[i]=sum[i-1]+mbs[i];
	}
}
ll k;
ll solve(ll n,ll m){
	n/=k;
	m/=k;
	if(n>m) swap(n,m);
	ll ans=0;
	ll nxt;
	for(int i=1;i<=n;i=nxt+1){
		nxt=min(n/(n/i),m/(m/i));
		ans+=(sum[nxt]-sum[i-1])*(n/i)*(m/i);
	}
	return ans;
}
int main(){
	int cas=1;
	int t;
	mobious();
	scanf("%d",&t);
	ll a,b,c,d;
	while(t--){
		scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&k);
		printf("Case %d: ",cas++);
		if(k==0) puts("0");
		else
		printf("%lld\n",solve(b,d)-solve(min(b,d),min(b,d))/2);
	}
	return 0;
}