【 HDU - 1796】How many integers can you find (容斥原理,二进制枚举或者dfs)

39 篇文章 0 订阅
7 篇文章 0 订阅

题干:

  Now you get a number N, and a M-integers set, you should find out how many integers which are small than N, that they can divided exactly by any integers in the set. For example, N=12, and M-integer set is {2,3}, so there is another set {2,3,4,6,8,9,10}, all the integers of the set can be divided exactly by 2 or 3. As a result, you just output the number 7.

Input

  There are a lot of cases. For each case, the first line contains two integers N and M. The follow line contains the M integers, and all of them are different from each other. 0<N<2^31,0<M<=10, and the M integer are non-negative and won’t exceed 20.

Output

  For each case, output the number.

Sample Input

12 2
2 3

Sample Output

7

题目大意:

给定n和一个大小为m的集合,集合元素为非负整数。为1...n内能被集合里任意一个数整除的数字个数。n<=2^31,m<=10

输入n和m,接下来m个数。多组输入数据。

解题报告:

    就是个简单的容斥啦~直接暴力、。但是用二进制的话时间挺紧凑的吧感觉。。。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int MAX = 2e5 + 5;
ll a[MAX];
ll LCM(ll a,ll b) {
	return (a*b)/__gcd(a,b);
}
int main()
{
	ll n,m,tmp,tot,ans;
	while(~scanf("%lld%lld",&n,&m)) {
		tot=ans=0;
		for(int i = 1; i<=m; i++) {
			scanf("%lld",&tmp);
			if(tmp != 0) a[++tot] = tmp; 
		}
		for(int i = 1; i<=(1<<tot)-1; i++) {
			ll k = 0,lcm = 1;
			for(int j = 0; j<=tot-1; j++) {
				if(i & (1<<j)) {
					k++;
					lcm =LCM(lcm,a[j+1]);
				}
			}
			if(k & 1) ans += (n-1) / lcm;
			else ans -= (n-1) / lcm;  
		}
		
		printf("%lld\n",ans);
	}


	return 0 ;
 }

总结:

  注意判0啊,不然就会出现Runtime Error(INTEGER_DIVIDE_BY_ZERO)、、、其实想想也是嘛,因为gcd没事,但是lcm的时候有问题,比如lcm(0,100) return (0*100)/100

这题还可以用dfs写:(234ms)

//234ms
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
int n,m,cnt;
ll ans,a[30];
ll LCM(ll a,ll b) {
	return (a*b)/__gcd(a,b);
}
void dfs(int cur,ll lcm,int id) {
	lcm=LCM(a[cur],lcm);  //递归找两个数的最小公倍数(其中一个数还是集合里面上一个两个数的最小公倍数)所以lcm表示的真实意义是很多个数的最小公倍数
	if(id&1) ans+=(n-1)/lcm;     //因为这题并不包含n本身,所以用n-1
	else ans-=(n-1)/lcm;
	for(int i=cur+1; i<cnt; i++)
	        dfs(i,lcm,id+1);  //id+1是表示如果上一次是奇数个数的倍数那么这次就是找的是偶数个数的倍数
}

int main() {
	while(~scanf("%d%d",&n,&m)) {
		cnt=0;
		int x;
		while(m--) {
			scanf("%d",&x);
			if(x!=0)
				a[cnt++]=x;
		}
		ans=0;
		for(int i=0; i<cnt; i++)
			dfs(i,a[i],1);
		printf("%lld\n",ans);
	}
	return 0;
}

另一种dfs:(218ms)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
int n,m,cnt;
ll ans,a[30];
ll LCM(ll a,ll b) {
	return (a*b)/__gcd(a,b);
}

void dfs(int cur,ll lcm,int id) {
	if(cur == cnt) {
		if(id == 0) return ;
		if(id&1) ans += (n-1)/lcm;
		else ans-=(n-1)/lcm;
		return ;
	}
	dfs(cur+1,lcm,id);
	dfs(cur+1,LCM(lcm,a[cur+1]),id+1);
}

int main() {
	while(~scanf("%d%d",&n,&m)) {
		cnt=0;
		int x;
		while(m--) {
			scanf("%d",&x);
			if(x!=0)
				a[++cnt]=x;
		}
		ans=0;
		dfs(1,a[1],1);
		dfs(1,1,0);
		printf("%lld\n",ans);
	}
	return 0;
}

附:

一种新奇的dfs的思路:(31msAC)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值