容斥原理——二进制枚举与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   
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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" <

 

附上二进制枚举代码:

#include 
#define ll long long
ll prime[25];
ll l,r;
int sum;
ll solve()
{
	ll ans = 0;
	for(int i = 1;i < (1< 1) prime[sum++] = n;
			printf("Case #%d: %lld\n", cas,r-l-solve());
			if(cas != t) printf("\n");
		}
	}
	//cout << "AC" <

 

你可能感兴趣的:(数论,DFS)