题目大意:判断2到m有多少个数字是合法的。
对于给定的n个数,是合法的;其他数字若是合法,那么必须存在2到n-1的约数,且这些约数都是合法的。
暴力求解:从小到大枚举2到m,如果约数都合法,标记该数字合法;如果遇到一个不合法的约数,则不标记合法。
需要从小到大确定是否合法,保证用到的约数都是更小的、已确定是否合法。时间复杂度:
#include <stdio.h> #include <string.h> #define N 300005 int t, n, m, i, j, k, ans, f[N]; int main(){ scanf("%d", &t); while(t--){ scanf("%d%d", &n, &m); memset(f, ans=0, sizeof(f)); for(i=1; i<=n; i++){ scanf("%d", &k); f[k] = 1; } for(i=2; i<=m; i++){ if(f[i]) ans++; else{ for(k=0, j=2; j*j<=i; j++){ if(i%j) continue; k = 1; if(!f[j] || !f[i/j]) break; } if(k && j*j > i) ans++, f[i] = 1; }//有约数且约数都合法 } printf("%d\n", ans); } return 0; }
筛选法求素数:用筛法预处理出每个数字的约数个数(被筛掉次数),保存到数组c,如c[i]表示i的约数个数(2到n-1),如果是质数,约数设为无穷大。
题目要求,所有约数都是合法的;那我们就用合法的数字对2到m的数字进行筛选法求素数;如果“约数”个数恰好根c一致,那么全部约数都是合法的。(质数永不合法,除非属于集合S)。
时间复杂度:
#include <stdio.h> #include <string.h> #define N 300005 int t, n, m, i, j, k, ans, c[N], f[N]; int main(){ for(i=2; i<N; i++){ if(c[i] == 0) c[i] = N;//质数无真因子 for(j=i+i; j<N; j+=i) c[j]++; }//统计约数个数 scanf("%d", &t); while(t--){ scanf("%d%d", &n, &m); memcpy(f, c, sizeof(f)); for(i=1; i<=n; i++){ scanf("%d", &k); ans = f[k] = 0;//约数被筛完合法 } for(i=2; i<=m; i++){ if(f[i] > 0) continue;//不合法 ans++; for(j=i+i; j<=m; j+=i) f[j]--; }//合法的数能被合法的数筛完 printf("%d\n", ans); } return 0; }