容斥原理的简单应用就是去重复。一般来说,从全局考虑要比从局部考虑要直观、方便的多,但是这毕竟是忽略掉细节的,有时候就会造成错误。所以,在应用容斥原理时,首先会简单的从整体上算一遍,然后再考虑局部,减去多算的,加上多减的,如此反复。但是容斥原理也有一套复杂的公式,详见<<组合数学>>,容斥原理一章。
下面具体看一道题,熟悉容斥原理的思想,HDOJ:4135,时空转移(点击打开链接),题目如下:
Co-prime
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1563 Accepted Submission(s): 603
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.
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 <= 10
15) and (1 <=N <= 10
9).
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: 10HintIn the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.
就是求a~b之间,有多少个数与N是互素的。
分析:
先考虑暴力是否可行,但是看到题目中给出的数据范围,可知一定会超时的。所以,直接根据互素的定义去求是不可行的,那么换一种思路,如果我们知道了不互素的,那么答案就是总数减去不互素的,继续思考如果转而去求不互素的数量可行吗?我们知道一个数可以被分成多个因子相乘的形式,那么很明显了,如果一个数要和它不互素,那必定是其因子的倍数,这样一来我们就可以很快的写出小于它的哪些数和它不互素,但是我们发现对于不同因子的倍数,有可能是相同的,比如因子2和3,会出现2的三倍等于3的两倍,于是这里还的应用容斥原理,加奇减偶,去掉重复加上的。最后,问题转化成了求(b-func(b)) - (a-1-func(a-1)),func(b)代表1~b中与N不互素的个数,自然b-func(b)就代表的是1~b中与N互素的个数。
源代码:
#include<cstdio>
#include<cstring>
using namespace std;
#define LL __int64
const int MAXN = 70;
LL prime[MAXN];
LL func(LL up, int m) // 返回1~up中包含这m个因子倍数的数的个数,即与N不互素的个数
{
LL ans=0, tmp, i, j, flag;
for(i=1; i<(LL)(1<<m); ++i) // 用二进制来1,0来表示第几个素因子是否被用
{
tmp=1, flag=0;
for(j=0; j<m; ++j)
if(i & ((LL)(1<<j))) // 判断第几个因子目前被用到
++flag, tmp*=prime[j];
if(flag & 1) // 容斥原理,奇加偶减
ans += up/tmp;
else
ans -= up/tmp;
}
return ans;
}
int factor(LL n) // 对n进行素因子分解
{
int num = 0;
for(LL i=2; i*i<=n; ++i)
if(n && n%i==0)
{
prime[num++] = i;
while(n && n%i==0)
n /= i;
}
if(n > 1)
prime[num++] = n;
return num;
}
int main()
{
int T, t=0;
LL n, a, b;
scanf("%d", &T);
while(T--)
{
int num = 0;
scanf("%I64d%I64d%I64d", &a, &b, &n);
num = factor(n);
LL ans = (b-func(b,num)) - (a-1-func(a-1,num)); // 取a-1是因为需要包含a
printf("Case #%d: %I64d\n", ++t, ans);
}
return 0;
}
其它的相关题目还有,HDOJ:4390,2841,1695,1796。