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;

}

  





你可能感兴趣的:(simple)