1. 程式人生 > >Co-prime(容斥原理)

Co-prime(容斥原理)

Co-prime

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3495    Accepted Submission(s): 1381


Problem Description Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N.
Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than 1 or, equivalently, if their greatest common divisor is 1. The number 1 is relatively prime to every integer.
Input The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 1015
) and (1 <=N <= 109).
Output For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.
Sample Input 2 1 10 2 3 15 5
Sample Output Case #1: 5 Case #2: 10 HintIn the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.
Source

這道題確實應該好好的寫一篇題解了。

如果是求小於 n 且與 n 互質的數的個數,可以用尤拉函式解決,但是這道題a,b的範圍可能大於 k ,所以應該這麼用容斥原理來做:

求 1~a-1 中與 k 互質的數,再求 1 ~ b 中與 k 互質的數,後者減去前者就是答案。

然後就是求 1 ~ n 中與 k 互質的數有多少,我們可以反著,先求  1 ~ n 中與 k 不互質的數有多少。

這點求法再拉出來細說:先把 k 分解質因數,存在一個數組中。

舉個例子,比如 k 的質因子有 2,3,5。那麼2、3、5的倍數都不和 k 互質,另外還沒有完,可能有重複的地方,比如6,既是2的倍數又是3的倍數,前面用 k/2 + k/3 的時候多減了,這個時候要加上 k / (2*3)。同理,10,15這一類數都應該加上。但是還有類似於30這樣的數,它是2,3,5的倍數,減的時候又多減了。

然後我們會發現,出現奇數個數,就用加法,偶數個數用減法。

最後的式子是這樣的:k / 2 + k / 3 + k / 5 - k / (2 * 3) - k / (3 * 5) - k / (2 * 5) + k / (2 * 3 * 5)

有點長,看一下容易發現我說的奇偶的規律。

但是要怎麼取算這個又出現問題了。

這裡提供二進位制的方法,個人覺得比較好理解。

設質因數的個數為m。

有一個浮動的數字,從1 ~ m依次遞增,它的二進位制的每一位表示用了哪些數字,比如5(101),其二進位制的第一位和第三位(倒著數)是1,則它表示用了第一個質因數和第三個質因數。就是這個意思,這樣就能發現:從1到m遍歷一遍,就把所有的可能都包括了。

寫的挺累的,有錯誤謝謝指正,轉載說明出處。

下面看一下程式碼理解一下:

#include <cstdio>
int p[1000000];
int num;
__int64 a,b,k;
void pr(int x)		//求x的質因子
{
	num = 0;
	for (int i = 2 ; i * i <= x ; i++)
	{
		if (x % i == 0)
		{
			p[num++] = i;
			while (x % i == 0)
				x /= i;
		}
	}
	if (x > 1)
		p[num++] = x;
}
__int64 solve(__int64 n)		//1~n中不與k互質的數 
{
	__int64 ans = 0;
	for (__int64 i = 1; i < (__int64)1 << num ; i++)		//其二進位制位為1,表示這些質因數被用到 
	{
		int ant = 0;		//用奇數個質因數加,偶數個減
		__int64 t = 1;
		for (int j = 0 ; j < num ; j++)
		{
			if (((__int64)1 << j) & i)
			{
				t *= p[j];
				ant++;
			}
		}
		if (ant & 1)		//奇數加
			ans += n / t;
		else
			ans -= n / t;
	}
	return ans;
}
int main()
{
	int u;
	int Case = 1;
	scanf ("%d",&u);
	while (u--)
	{
		scanf ("%I64d %I64d %I64d",&a,&b,&k);
		pr(k);
		printf ("Case #%d: ",Case++);
		printf ("%I64d\n",b-(a-1)-(solve(b)-solve(a-1)));
	}
	return 0;
}