【题目】
【分析】
20 pts:
直接枚举。时间复杂度:O ( n m ) (nm) (nm)。
另外 40 pts:
过这 40    p t s 40\;pts 40pts,我们需要知道一点小知识:
- 1 1 1 到 n n n 的正整数中,能被 x x x 整除的数的个数 n u m x = n x num_x=\frac{n}{x} numx=xn(这里是整除,下同);
- 1 1 1 到 n n n 的正整数中,同时被 x x x、 y y y 整除的数个数 n u m x    a n d    y = n l c m ( x , y ) num_{x\; and\; y}=\frac{n}{ lcm(x,y)} numxandy=lcm(x,y)n( l c m lcm lcm 为最小公倍数)。
- 依此类推,同时被 x 1 , x 2 , . . . , x i x_1,x_2,...,x_i x1,x2,...,xi 整除的数的个数为 n l c m ( x 1 , x 2 , . . . , x i ) \frac{n}{lcm(x_1,x_2,...,x_i)} lcm(x1,x2,...,xi)n
现在回到这道题来
将题目中的式子转化一下: x ≡ 17 ( m o d    a i )    → x    m o d    a i = 17    m o d    a i    → ( x − 17 )    m o d    a i = 0 x≡17 (mod \;a_i)\;\rightarrow x\; mod \;a_i=17 \;mod \;a_i\;\rightarrow(x−17) \;mod \;a_i=0 x≡17(modai)→xmodai=17modai→(x−17)modai=0(注意已保证 a i > 17 a_i>17 ai>17,所以 17    m o d    a i = 17 17 \;mod \;a_i=17 17modai=17),于是问题变为求 0 0 0 到 m − 17 m−17 m−17 整数中至少能被 a a a 中一个数整除的有多少个。
根据容斥原理, ∣ A ∪ B ∣ = ∣ A ∣ + ∣ B ∣ − ∣ A ∩ B ∣ |A∪B|=|A|+|B|−|A∩B| ∣A∪B∣=∣A∣+∣B∣−∣A∩B∣( ∣ A ∣ |A| ∣A∣ 表示集合 A A A 元素个数),可得 1 1 1 到 n n n 的正整数中,能被 x x x 或 y y y 整除的数个数 n u m x    o r    y = n u m x + n u m y − n u m x    a n d    y = n x + n y − n l c m ( x , y ) num_{x\; or\; y}=num_x+num_y-num_{x \;and\; y}=\frac{n}{x}+\frac{n}y-\frac n{lcm(x,y)} numxory=numx+numy−numxandy=xn+yn−lcm(x,y)n。对于三个数 x x x、 y y y、 z z z 的情况,只要类比一下,画画图就可以知道:
n u m x    o r    y    o r    z = n u m x + n u m y + n u m z − n u m x    a n d    y − n u m x    a n d    z − n u m y    a n d    z + n u m x    a n d    y    a n d    z = n x + n y + n z − n l c m ( x , y ) − n l c m ( x , z ) − n l c m ( y , z ) + n l c m ( x , y , z ) num_{x\; or\; y\; or\; z}=num_x+num_y+num_z-num_{x \;and\; y}-num_{x\; and\; z}-num_{y \;and\; z}+num_{x \;and \;y \;and\; z}=\frac{n}{x}+\frac{n}{y}+\frac{n}{z}-\frac{n}{lcm(x,y)}-\frac{n}{lcm(x,z)}-\frac{n}{lcm(y,z)}+\frac{n}{lcm(x,y,z)} numxoryorz=numx+numy+numz−numxandy−numxandz−numyandz+numxandyandz=xn+yn+zn−lcm(x,y)n−lcm(x,z)n−lcm(y,z)n+lcm(x,y,z)n
时间复杂度:O ( l o g    m ) (log\;m) (logm)
100 pts:
容斥原理是可以扩展到 n n n 个集合的,具体就是口诀:奇加偶减
这时我们需要用个 d f s dfs dfs 枚举每个 a i a_i ai 选与不选,若选出 k k k 个数,且 k k k 为奇数,则 a n s ans ans 加上 m l c m \frac{m}{lcm} lcmm(这里的 l c m lcm lcm 就是选出的数的最小公倍数,下同),若为偶数 a n s ans ans 减去 m l c m \frac{m}{lcm} lcmm。
当然,这个算法复杂度还比较大,还要加上优化,包括可行性剪枝(选出的数的 l c m lcm lcm 要小于等于 m m m。由于 a i > 17 a_i>17 ai>17,该剪枝十分有效)、优化搜索顺序(先搜大数)。
时间复杂度:O ( 2 n ∗ l o g    m ) (2^n*log\;m) (2n∗logm)(实际小得多)
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 105
#define ll long long
using namespace std;
int n,m,ans,a[N];
bool comp(int a,int b) {return a>b;}
ll Lcm(ll a,ll b) {return a*b/__gcd(a,b);}
void dfs(int x,int num,ll lcm)
{
if(lcm>m) return;
if(x>n&&lcm==1) {ans++;return;}
if(x>n) {num&1?ans+=m/lcm:ans-=m/lcm;return;}
dfs(x+1,num,lcm),dfs(x+1,num+1,Lcm(lcm,a[x]));
}
int main()
{
// freopen("count.in","r",stdin);
// freopen("count.out","w",stdout);
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1,comp);
m-=17,dfs(1,0,1);
printf("%d",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}