数论——素数和反素数

素数

素数一般判定方法

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)这一性质进行枚举。

 

posted @ 2020-02-25 22:26  Ldler  Views(375)  Comments(0Edit  收藏  举报