链接:https://www.nowcoder.com/acm/contest/135/A
来源:牛客网
若一个集合A内所有的元素都不是正整数N的因数,则称N与集合A无关。
给出一个含有k个元素的集合A={a1,a2,a3,...,ak},求区间[L,R]内与A无关的正整数的个数。
保证A内的元素都是素数。
输入描述:
输入数据共两行:
第一行三个正整数L,R,k,意义如“题目描述”。
第二行k个正整数,描述集合A,保证k个正整数两两不相同。
输出描述:
输出数据共一行:
第一行一个正整数表示区间[L,R]内与集合A无关的正整数的个数
示例2
说明
对于30%的数据:1<=L<=R<=10^6
对于100%的数据:1<=L<=R<=10^18,1<=k<=20,2<=ai<=100
分析:直接计算无关的数,要枚举计算,量很大。所以考虑转化成计算有关的数,然后用总数减去有关的数就是无关的数
如我们要计算l到r的无关的数,就等于r-l+1-(f(r)-f(l-1)),其中f(x)代表1到x的有关的数
然后计算有关的数,考虑计算有关数的时候会出现像6这样既是2的倍数又是3的倍数的数,所以我们在计算的过程中要用容斥原理来去重
这里我们考虑用二进制状态的方法来计算每个数的容斥
last=(1<<k)-1,然后通过与(1<<j)(0<=j<k)进行二进制与运算来保证循环的过程中,我们依次计算了a[0],a[0]*a[1],...,a[0]*a[1]*...*a[k-2]*a[k-1]的倍数
计算出这些数有多少个倍数后用容斥原理的奇加偶减计算最后的结果
#include <map> #include <set> #include <stack> #include <cmath> #include <queue> #include <cstdio> #include <vector> #include <string> #include <cstring> #include <iostream> #include <algorithm> #define debug(a) cout << #a << " " << a << endl using namespace std; const int maxn = 25; const int mod = 1e9 + 7; typedef long long ll; ll a[maxn], k; ll solve( ll n ) { //求1到n区间和集合a有关的数的个数 if( n == 0 ) { //特判一下1到0区间不存在,所以满足条件的数的个数当然是0 return 0; } ll ans = 0, last = ( 1 << k ) - 1; //用二进制数的二进制位表示集合a里每个数的使用状态,1表示使用了, //0表示没使用,对应二进制的第一位对应的是第一个数的使用状态, //last表示的是k个数的最后一个枚举方式1..1,(k个1) for( ll sta = 1; sta <= last; sta ++ ) { //这里从状态从1枚举到last,刚好把k个数的全部状态枚举了一遍 ll cnt = 0; //记录当前枚举的状态里使用的数的个数 ll flag = 0; ll tmp = 1; //tmp存的是枚举到的数的最小公倍数 for( ll j = 0; j < k; j ++ ) { if( sta&(1<<j) ) { cnt ++; tmp *= a[j]; if( tmp > n ) { flag = 1; break; } } } if( flag ) { continue; } if( cnt&1 ) { //根据容斥定理,求k个集合的并集的元素个数时是奇加偶减 ans += n/tmp;//我们要求的是这cnt个数,假设是a2,a4,a5,要求这些数在1到n的公共倍数有多少个, //就要先求出这cnt个数的lcm然后n/lcm即可,这里题目保证了a数组每个数都是质数,所以cnt个数的乘积即是对应的lcm } else { ans -= n/tmp; } } return ans; } int main() { ll le, ri; while( cin >> le >> ri >> k ) { for( ll i = 0; i < k; i ++ ) { cin >> a[i]; } cout << ri-le+1-(solve(ri)-solve(le-1)) << endl; } return 0; }