题目描述
题目链接:能被整除的数
问题分析
读题发现问题模型是求n中是p1,p2,…pm中至少一个整除的整数个数。
我们知道,n/p1便是1~n中能被p1整除的个数。依次,n/pi 便是1-n中能被pi整除的个数。
例如:1-10 中能被2整除的数是2,4,6,8,10这5个,也就是10/2 = 5,1-10中能被3整除的数是3,6,9这3个数,也就是10/3的整数部分。那么1~10中能被2、3中至少一个整除的数的个数并不是3+5= 8 ,其中6这个数既能被2整除,也能被3整除。那么这个就需要减去。
这就是一个典型的容斥原理。
我们从特殊到一般的推导过程如下图所示:
因此我们需要计算出各种情况下的个数,然后有规律的累加起来,便是此问题的解。
那么这些情况如何找?我们发现每一项就是曾经在深度优先搜索里学过的m个数中选x个数的问题,我们将这x个数组合枚举出来的时候,将其累乘出一个数nowvalue,然后计算n/nowvalue便可得这个数列所对应的数能整除的个数。
那么我们只需要通过深搜将这些组合数的情况一一搜出来并计算就可以了。
再来说说这个程序的时间复杂度。根据上图中最后一行,我们发现求解式子的项数是2^m - 1,然后每一项的深度最坏情况下是m,因此本题的算法时间复杂度是O(2 ^ (m+1)),而m<=16.不会超时。
代码实现
#include<bits/stdc++.h>
using namespace std;
bool vis[18];
int n,m;
int a[18];
int temp;
void dfs(int k,int x,int pos,int nowvalue){
if(nowvalue > n) temp += 0;
else if(x > k) temp += n/nowvalue;
else {
for(int i = pos; i<= m; i++){
if(!vis[i] && 1LL* nowvalue * a[i] <= n){
vis[i] = true;
dfs(k, x+1,i+1, nowvalue * a[i]);
vis[i] = false;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i<= m; i++){
scanf("%d",&a[i]);
}
int ans = 0,k = 1;
for(int i = 1;i<= m; i ++,k = k * -1){
temp = 0;
dfs(i,1,1,1);
ans = ans + k * temp;
}
printf("%d",ans);
return 0;
}