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
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
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; }