ACM-容斥原理

容斥原理的简单应用就是去重复。一般来说,从全局考虑要比从局部考虑要直观、方便的多,但是这毕竟是忽略掉细节的,有时候就会造成错误。所以,在应用容斥原理时,首先会简单的从整体上算一遍,然后再考虑局部,减去多算的,加上多减的,如此反复。但是容斥原理也有一套复杂的公式,详见<<组合数学>>,容斥原理一章。

下面具体看一道题,熟悉容斥原理的思想,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.
 

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: 10
Hint
In 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。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值