HOJ 2576 HOJ 2577 Simple Computing I & II 容斥原理

两题的链接先给上:

http://acm.hit.edu.cn/hoj/problem/view?id=2576

http://acm.hit.edu.cn/hoj/problem/view?id=2577

 

下面两题都是很经典的容斥原理的题目,自己做的时候有点瞎yy,所以下面的表达不太严谨。。。

HOJ 2576 Simple Computing

My Tags 容斥原理   (Edit)
  Source : ACMGroup
  Time limit : 1 sec   Memory limit : 32 M

Submitted : 391, Accepted : 123

Given n integers x1 x2 ... xn, you should count how many intergers from 1 to m that can be divided by at least one of them.

 Input

 The first line is an integer c which shows the number of cases. Each case contains 2 lines. The first line has 2 integers n and m. The second line contains n integers. (0<n<11, 0<m<2^31, 0<xi<100)

 Output 

For each case print the answer in a single line.

 Sample Input

2
1 10
2
2 20
3 4

Sample Output

5
10

题目大意:
  给出一组数a1...an,问从1到m中能有多少个数能够最少能被这组数中的一个整除

分析:
容斥原理可以解决1-n中与m互质的数的个数问题,做法是把m分解成几个素因子,然后利用容斥原理求解。由于p1..pk都是素数,所以
gcd(pi,pj) == 1。
而这题变成了n个数(包含合数)。所以在求解过程中乘完之后还得除以最大公约数。不然如3,6,m =18时,答案是6。在计算的时候,6,12很明显能够同时被3,6整除。

代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define debug puts("here")

typedef long long ll;

const int X = 105;

int size;
int di[X];

int gcd(int x,int y){
	return x==0?y:gcd(y%x,x);
}

void cal(ll n){
	ll sum = 0;
	for(int i=1;i<(1<<size);i++){
		ll s = 1;
		bool ok = false;
		for(int j=0;j<size;j++)
			if( i & (1<<j) ){
				s = s/gcd(s,di[j])*di[j];
				ok = !ok;
			}
		if(ok)
			sum += n/s;
		else
			sum -= n/s;
	}
	cout<<sum<<endl;
}


int main(){
#ifndef ONLINE_JUDGE
	freopen("suma.txt","r",stdin);
#endif
	int ncase,m;
	cin>>ncase;
	while(ncase--){
		scanf("%d%d",&size,&m);
		for(int i=0;i<size;i++)
			scanf("%d",&di[i]);
		cal(m);
	}
	return 0;
}

  

另外变形的一题

Simple Computing II

My Tags 容斥原理   (Edit)
  Source : ACMGroup
  Time limit : 1 sec   Memory limit : 32 M

Submitted : 173, Accepted : 63

Given n integers x1 x2 ... xn, you should count how many intergers from 1 to m that can be divided exactly by only one of them.

Input

The first line is an integer c which shows the number of cases. Each case contains 2 lines. The first line has 2 integers n and m. The second line contains n integers. (0<n<11, 0<m<2^31, 0<xi<100)

Output

For each case print the answer in a single line.

Sample Input

2
1 10
2
2 20
3 4

Sample Output

5
9

题目:
  给出n个数,现在问从1到m只能够被这n个数中的一个数整除的个数

分析:
  我们上一题是最少能够被这n个数中的一个整除,所以我们可以很容易用容斥解决掉。
  那么这题其实就相当于那题的稍微变形,求的是只能够被n个数中的一个整除。直观上
  来看,我们需要把两个或者两个以上的去掉。所以还是用容斥来做,只不过在加减的
  时候需要控制一下就好了。
  1.在只有两个的时候,减掉的个数*2。
  2.在加三的时候,由于在减二时多减了,所以我们得要加回来,然后再减掉自己那个部分,刚好是*3。
  3.同样,在拥有i个数的时候:
    i 奇数 + s*i
    i 偶数 - s*i

代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define debug puts("here")

typedef long long ll;

const int X = 105;

int size;
int di[X];

int gcd(int x,int y){
	return x==0?y:gcd(y%x,x);
}

void cal(ll n){
	ll sum = 0;
	for(int i=1;i<(1<<size);i++){
		ll s = 1;
		int ok = 0;
		for(int j=0;j<size;j++)
			if( i & (1<<j) ){
				s = s/gcd(s,di[j])*di[j];
				ok ++;
			}
		if(ok&1)
			sum += n/s*ok;
		else
			sum -= n/s*ok;
	}
	cout<<sum<<endl;
}


int main(){
#ifndef ONLINE_JUDGE
	freopen("suma.txt","r",stdin);
#endif
	int ncase,m;
	cin>>ncase;
	while(ncase--){
		scanf("%d%d",&size,&m);
		for(int i=0;i<size;i++)
			scanf("%d",&di[i]);
		cal(m);
	}
	return 0;
}

  





posted @ 2013-01-17 21:04  yejinru  阅读(630)  评论(0编辑  收藏  举报