题目描述
给定一个长度为n的序列a1..an,求m以内的不能被a1..an中任意一个ai整除的正整数有多少个?
输入
第一行两个数n,m
接下来一行n个数,a1..an
输出
共一个数,即m以内的不能被a1..an中任意一个ai整除的正整数有多少个。
样例输入
3 2015
4 5 6
样例输出
1075
提示
对于 30% 的数据,1≤m≤100000
对于另外 30% 的数据,n=3
对于 100% 的数据,1≤n≤20, 1≤m≤10^18, 1≤ ai≤10^9
思路很简单,容斥原理,但是状压一直超时,卑微
丧心病狂的剪枝优化
代码1:无剪枝状压
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[30];
ll n,m;
ll lcm(ll a,ll b){
return a/__gcd(a,b)*b;
}
bool judge(ll a,ll b){
ll c=m/a;
return b<=c;
}
int main(){
scanf("%lld%lld",&n,&m);
int N=0;
for(int i=0;i<n;i++){
ll x;
scanf("%lld",&x);
if(x<=m) a[N++]=x;
}
ll ans=0;
for(int i=1;i<(1<<N);i++){
int cnt=0;
ll down=1;
for(int j=0;j<N;j++){
if(i&(1<<j)){
cnt++;
if(judge(down,a[j]/__gcd(down,a[j])))
down=lcm(down,a[j]);
else{
down=m+1;
break;
}
}
}
if(down==m+1) continue;
if((cnt&1)) ans+=m/down;
else ans-=m/down;
}
printf("%lld\n",m-ans);
return 0;
}
代码2:剪枝+状压
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[30];
int n;
ll m;
ll gcd;
ll lcm(ll a,ll b){
return a/gcd*b;
}
bool judge(ll a,ll b){
return b<=m/a;
}
bool cmp(ll a,ll b){
return a>b;
}
int main(){
scanf("%d%lld",&n,&m);
int N=0;
ll x;
bool flag=true;
for(int i=0;i<n;i++){
scanf("%lld",&x);
if(x<=m) a[N++]=x;
if(x==1) flag=false;
}
sort(a,a+N,cmp);
ll ans=0;
if(flag){
for(int i=1;i<(1<<N);i++){
int cnt=0;
ll down=1;
for(int j=0;j<N;j++){
if(i<(1<<j)) break;
if(i&(1<<j)){
cnt++;
gcd=__gcd(down,a[j]);
if(judge(down,a[j]/gcd))
down=lcm(down,a[j]);
else{
down=m+1;
break;
}
}
}
if(down==m+1) continue;
if((cnt&1)) ans+=m/down;
else ans-=m/down;
}
}
else ans=m;
printf("%lld\n",m-ans);
return 0;
}
代码3:无剪枝+dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll m,ans;
ll a[30];
bool judge(ll a,ll b){
return b<=m/a;
}
void dfs(int now,int num,ll down){
if(now==n){
if(num&1) ans+=m/down;
else if(num!=0) ans-=m/down;
return ;
}
ll gcd=__gcd(down,a[now]);
if(judge(down/gcd,a[now])) dfs(now+1,num+1,down/gcd*a[now]);
dfs(now+1,num,down);
}
int main(){
scanf("%d%lld",&n,&m);
for(int i=0;i<n;i++) scanf("%lld",&a[i]);
ans=0;
dfs(0,0,1);
printf("%lld\n",m-ans);
return 0;
}
代码4:剪枝+dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll m,ans;
ll a[30];
bool cmp(ll a,ll b){
return a>b;
}
bool judge(ll a,ll b){
return b<=m/a;
}
void dfs(int now,int num,ll down){
if(now==n){
if(num&1) ans+=m/down;
else if(num!=0) ans-=m/down;
return ;
}
ll gcd=__gcd(down,a[now]);
if(judge(down/gcd,a[now])) dfs(now+1,num+1,down/gcd*a[now]);
dfs(now+1,num,down);
}
int main(){
scanf("%d%lld",&n,&m);
int cnt=0;
bool flag=true;
for(int i=0;i<n;i++) {
ll x;
scanf("%lld",&x);
if(x<=m) a[cnt++]=x;
if(x==1) flag=false;
}
n=cnt;
if(flag){
sort(a,a+n,cmp);
ans=0;
dfs(0,0,1);
}
else{
ans=m;
}
printf("%lld\n",m-ans);
return 0;
}