题目
链接:https://ac.nowcoder.com/acm/problem/16513
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
若一个集合A内所有的元素都不是正整数N的因数,则称N与集合A无关。
给出一个含有k个元素的集合A={a1,a2,a3,…,ak},求区间[L,R]内与A无关的正整数的个数。
保证A内的元素都是素数。
输入描述:
输入数据共两行:
第一行三个正整数L,R,k,意义如“题目描述”。
第二行k个正整数,描述集合A,保证k个正整数两两不相同。
输出描述:
输出数据共一行:
第一行一个正整数表示区间[L,R]内与集合A无关的正整数的个数
示例1
输入
复制
1 10 4
2 3 5 7
输出
复制
1
示例2
输入
复制
2 10 4
2 3 5 7
输出
复制
0
说明
对于30%的数据:1<=L<=R<=10^6
对于100%的数据:1<=L<=R<=10^18,1<=k<=20,2<=ai<=100
题解
L和R数据规模1e18,很明显是不可能从L到R遍历暴力求解,注意到K<=20,很明显就从20个数下手,因此我们的思路就是,排除所有的倍数,剩下的就是无关数了。
于是就考虑用容斥原理:
∣
A
1
∪
A
2
∪
.
.
.
∪
A
n
∣
=
∑
1
≤
i
≤
n
∣
A
i
∣
−
∑
1
≤
i
,
j
≤
n
∣
A
i
∩
A
j
∣
+
∑
1
≤
i
,
j
,
k
≤
n
∣
A
i
∩
A
j
∩
A
k
∣
−
.
.
.
.
.
.
\mid A_1 \cup A_2 \cup ... \cup A_n \mid=\sum_{1\le i\le n}{ \mid A_i \mid}-\sum_{1\le i,j\le n} {\mid A_i \cap A_j \mid}+\sum_{1\le i,j,k\le n}{ \mid A_i \cap A_j \cap A_k \mid}-......
∣A1∪A2∪...∪An∣=1≤i≤n∑∣Ai∣−1≤i,j≤n∑∣Ai∩Aj∣+1≤i,j,k≤n∑∣Ai∩Aj∩Ak∣−......
需要注意的是20个素数的积可能爆long long,因此需要判断与剪枝。
由于本人数学水平捉急,只会求0到n之间的倍数个数,所以L到R之间的倍数个数采用0~R - 0~L,枚举过程这里采用状压,感觉2^k的遍历状压很舒服
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include <cctype>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include <algorithm>
#define ll long long
#include<iomanip>
#define mms(a) memset(a,0,sizeof(a))
using namespace std;
ll l,r,k;
int a[21];
ll Find(ll N)
{
ll sum=0;
for(int i=1;i<(1<<k);i++)
{
ll cnt=0,cmp=1;
for(int j=0;j<k;j++)
{
if(i&(1<<j))
{
cnt++;
cmp*=a[j];
}
if(cmp>N)
{
cmp=0;
break;
}
}
if(cmp && cnt%2)
sum+=N/cmp;
else if(cmp)
sum-=N/cmp;
}
return N-sum;
}
int main( )
{
cin >> l >> r >> k;
for(int i=0;i<k;i++)
cin >> a[i];
cout << Find(r)-Find(l-1) << endl;
return 0;
}