数论——素数和反素数
素数
素数一般判定方法
bool isPrime(a) { if (a < 2) return 0; for (int i = 2; i * i <= a; ++i) if (a % i) return 0; return 1; }
但对于long long int的数,O(√n)的复杂度还是会时间超限。
此时要用Miller-Rabin 素性测试,但也只是极为可能是素数。
复杂度O(klog3n)。
#include <iostream> using namespace std ; #define rd(x) (rand()%(x)) typedef unsigned long long ll; ll pow_mod(ll a,ll b,ll r) { ll ans=1,buff=a; while(b) { if(b&1) ans=(ans*buff)%r; buff=(buff*buff)%r; b>>=1; } return ans; } bool test(ll n,ll a,ll d) { if(n==2) return true; if(n==a) return false; if(!(n&1)) return false; while(!(d&1)) d>>=1; ll t = pow_mod(a,d,n); while(d!=n-1&&t!=n-1&&t!=1){ t = t*t%n;//下面介绍防止溢出的办法,对应数据量为10^18次方; d<<=1; } return t == n-1||(d&1)==1;//要么t能变成n-1,要么一开始t就等于1 } bool isprime(ll n) { int a[] = {2,3,5,7};//或者自己生成[2,N-1]以内的随机数rand()%(n-2)+2 for(int i = 0; i <= 3; ++i){ if(n==a[i]) return true; if(!test(n,a[i],n-1)) return false; } return true; } int main() { int t,ans=0; ll N; for(cin >> t;t;t--){ cin >> N; cout << ((isprime(N))?"Yes":"No") <<endl; } }
注意:代码中longlong*longlong可能溢出,可以优化成快速乘。
反素数
定义:如果某个正整数 n满足如下条件,则称为是反素数: 任何小于 n的正数的约数个数都小于n的约数个数。
将该数进行质因子分解,我们可以得出n=p1k1p2k2...pnkn,所以因子个数为(k1+1)*(k2+1)..*(kn+1)
因为反素数的定义,可以得出反素数的两个特点
所以可以通过枚举,去确定反素数。
经典题型1:求因子数一定的最小数
题目链接: https://codeforces.com/problemset/problem/27/E
对于这种题,我么只要以因子数为 dfs 的返回条件基准,不断更新找到的最小值就可以了
#include <stdio.h> #define ULL unsigned long long #define INF ~0ULL ULL p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}; ULL ans; ULL n; // depth: 当前在枚举第几个素数。num: 当前因子数。 // temp: 当前因子数量为 num // 的时候的数值。up:上一个素数的幂,这次应该小于等于这个幂次嘛 void dfs(ULL depth, ULL temp, ULL num, ULL up) { if (num > n || depth >= 16) return; if (num == n && ans > temp) { ans = temp; return; } for (int i = 1; i <= up; i++) { if (temp / p[depth] > ans) break; dfs(depth + 1, temp = temp * p[depth], num * (i + 1), i); } } int main() { while (scanf("%llu", &n) != EOF) { ans = INF; dfs(0, 1, 1, 64); printf("%llu\n", ans); } return 0; }
经典题型2:求 n 以内因子数最多的数
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562
思路同上,只不过要改改 dfs 的返回条件。注意这样的题目的数据范围,我一开始用了 int,应该是溢出了,在循环里可能就出不来了就超时了。
上代码,0ms 过。注释就没必要写了上面写的很清楚了。
#include <cstdio> #include <iostream> #define ULL unsigned long long int p[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}; ULL n; ULL ans, ans_num; // ans 为 n 以内的最大反素数(会持续更新),ans_sum 为 ans // 的因子数。 void dfs(int depth, ULL temp, ULL num, int up) { if (depth >= 16 || temp > n) return; if (num > ans_num) { ans = temp; ans_num = num; } if (num == ans_num && ans > temp) ans = temp; for (int i = 1; i <= up; i++) { if (temp * p[depth] > n) break; dfs(depth + 1, temp *= p[depth], num * (i + 1), i); } return; } int main() { while (scanf("%llu", &n) != EOF) { ans_num = 0; dfs(0, 1, 1, 60); printf("%llu\n", ans); } return 0; }
两个题目都是利用n=p1k1p2k2...pnkn,所以因子个数为(k1+1)*(k2+1)..*(kn+1)这一性质进行枚举。