容斥原理——二进制枚举与dfs

本文介绍了容斥原理在解决集合问题中的应用,探讨了如何使用二进制枚举和深度优先搜索(DFS)方法,并结合实例展示了这两种方法在解决实际问题时的等效性。同时,提供了一个关于寻找与特定数互质的数的题目,附带了DFS和二进制枚举的代码实现。

容斥原理

对于两个集合,A\cup B=A+B-A\cap B

对于三个集合,A\cup B \cup C=A+B+C-A\cap B-A \cap C - B \cap C + A \cap B \cap C

以此类推即可,上式很好理解的

对于容斥原理,可以用二进制枚举,当然dfs也行,这两个复杂度差不多

二进制枚举

对于一个二进制数,每一位只有1和0两种情况,对应着取与不取

举个例子,一共有5件物品,那么最大值就选择(1<<5)-1 = 11111 = 31,如果从00001枚举到11111,也就是1---31,就能把所有情况都枚举出来了

题目

小明做题(一)

Time Limit: 4000/2000ms (Java/Others)

Problem Description:

小明刚上大学,报的是计算机专业,听说acm协会是一个玩思维,玩数学等等的好地方,一向数学成绩优秀的他果断参加了acm笔试,而且最终成绩还不错,便正式成为了一名acmer。有一天晚上,时不时出题人就出了这样一道题:给定一个区间(L,R)(L、R均为正整数)和一个正整数N,求该区间与N互质的数的个数。小明绞尽脑子还是想不出怎么快速解决这道题.请问你能帮助小明解决这道题吗


 


 

Input:

输入包含若干组数据。每组数据第一行包含一个T(1 <= T <= 100),表示T个测试样例,接下来的T行中每行包含三个正整数 L 、R 和 N(1 <= L < R <= 10^16,1 <= N <= 10^13)。

 

Output:

对于每个测试用例,打印L和R之间满足条件的整数个数。注意:每组数据内每个输出之间空一行。请遵循下面的输出格式。

Sample Input:

2
1 10 2
2 12 5 
2
10 18 3
12 15 4

Sample Output:

Case #1: 4

Case #2: 7
Case #1: 5

Case #2: 1

 

找出与n互质的数的个数并相减即可

附上DFS代码:

#include <cstdio>  
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
ll prime[100];
int sum = 0;
ll ans = 0,l,r;
void dfs(int now,ll mul,int num)
{
	if(now == sum)
	{
		if(num %2 == 0 && mul != 1)
			ans+=l/mul,ans-=r/mul;
		else if(mul != 1)
			ans-=l/mul,ans+=r/mul;
		return;
	}
	//cout << ans << endl;
	dfs(now+1,mul*prime[now],num+1);
	dfs(now+1,mul,num);
}
int main()
{
	int t;
	while(scanf("%d",&t)!=EOF)
	{
		for(int cas = 1;cas <= t;cas++)
		{
			ll n;
			sum = 0;
			ans = 0;
			scanf("%lld%lld%lld",&l,&r,&n);
			r--;
			for(ll j = 2;j*j<=n;j++){
				if(n%j==0){
					prime[sum++]=j;
					while(n%j==0) n/=j;
				}
			}
			if(n > 1) prime[sum++] = n;
			dfs(0,1,0);
			printf("Case #%d: %lld\n", cas,r-l-ans);
			if(cas != t)
				printf("\n");
		}	
	}
	//cout << "AC" <<endl;
	return 0;
}

 

附上二进制枚举代码:

#include <stdio.h>
#define ll long long
ll prime[25];
ll l,r;
int sum;
ll solve()
{
	ll ans = 0;
	for(int i = 1;i < (1<<sum);i++){
		ll tp = 1;
		int temp = 0;
		for(int j = 0;j < sum;j++)
			if((1<<j)&i){
				temp++;
				tp*=prime[j];
			}
		if(temp&1) ans-=l/tp,ans+=r/tp;
		else ans+=l/tp,ans-=r/tp;
	}
	return ans;
}
int main(){
	int t;
	while(scanf("%d",&t) !=EOF){
		for(int cas = 1;cas<=t;cas++){
			sum = 0;
			ll n;
			scanf("%lld%lld%lld",&l,&r,&n);
			r--;
			for(ll j = 2;j*j<=n;j++){
				if(n%j==0){
					prime[sum++]=j;
					while(n%j==0) n/=j;
				}
			}
			if(n > 1) prime[sum++] = n;
			printf("Case #%d: %lld\n", cas,r-l-solve());
			if(cas != t) printf("\n");
		}
	}
	//cout << "AC" <<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值