《挑战程序设计竞赛》2.6.2 数学问题-素数 AOJ0009 POJ3126 3421 3292 3641

AOJ0009

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0009

题意

求不大于n的素数个数。

思路

素数筛法可解,筛法过程中可顺便统计不大于n的素数个数。
另外这个题由于有多个测试数据,可预先求出题目所给数据范围的所有解。
素数筛法中我的写法里要注意数的范围,这个题中的i*i是可能超过int表示范围的,因而我提交了好几次都是RE,需要提前判断。

代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 1000000;

bool prime[N];
int num[N];

int main(void)
{
    fill(prime, prime+N, true);
    fill(num, num+N, 0);
    prime[1] = false;
    for (int i = 2; i < N; i ++) {
        num[i] = num[i-1];
        if (prime[i]) {
            num[i] ++;
            if (i > (int)sqrt((double)N)) continue;
            for (int j = i*i; j < N; j += i) {
                prime[j] = false;
            }
        }
    }

    int n;
    while (scanf("%d", &n) != EOF)
        printf("%d\n", num[n]);

    return 0;
}

POJ3126

http://poj.org/problem?id=3126

题意

给定两个四位素数a b,要求把a变换到b。
变换的过程要保证每次变换出来的数都是一个四位素数,而且当前这步的变换所得的素数与前一步得到的素数只能有一个位不同,而且每步得到的素数都不能重复。

思路

这个题其实主要考的是BFS,素数判定只是其中的条件判定依据。此处的素数构造用的是素数筛法。

代码

Source Code

Problem: 3126       User: liangrx06
Memory: 260K        Time: 16MS
Language: C++       Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 10000;
const int INF = 10000;

bool isPrime[N];

void initPrime()
{
    for (int i = 0; i < N; i ++)
        isPrime[i] = true;
    for (int i = 2; i < N; i ++) {
        if (isPrime[i]) {
            for (int j = i*i; j < N; j += i)
                isPrime[j] = false;
        }
    }
}

int d[N];

int exp10(int x)
{
    int res = 1;
    while (x--)
        res *= 10;
    return res;
}

int BFS(int begin, int end)
{
    int i, j;
    queue <int> q;
    for (i = 1000; i < N; i++)
        d[i] = INF;
    q.push(begin);
    d[begin] = 0;

    while( q.size() ) {
        int p = q.front();
        q.pop();
        if (p == end) break;

        for (i = 1; i <= 1000; i *= 10) {
            int m = (p/i) % 10;
            for (j = 0; j <= 9; j ++) {
                if (j == m) continue;
                if (j == 0 && i == 1000) continue;
                int n = p + (j-m) * i;
                if (isPrime[n] && d[n] == INF) {
                    q.push(n);
                    d[n] = d[p] + 1;
                }
            }
        }
    }

    return d[end];
}

int main(void)
{
    int t, begin, end;

    initPrime();
    cin >> t;
    while (t--) {
        cin >> begin >> end;
        int res = BFS(begin, end);
        if (res == INF)
            printf("Imossible\n");
        else
            printf("%d\n", res);
    }
    return 0;
}

POJ3421

http://poj.org/problem?id=3421

题意

给你一个数X,将X分解成1~X的因子数列,前一个数可以整数后一个数,求满足条件的最大链长以及有多少条这样长的链。

思路

容易想到因式分解,最大链长就是素数因子个数,链的个数即是全部素因子的排列数。
若有m个素因子x1,x2,…,xm,每个素因子的个数为c1,c2,…,cm,素因子个数相加为C,则全部素因子排列数为:
C!/(c1!c2!***cm!)

代码

Source Code

Problem: 3421       User: liangrx06
Memory: 208K        Time: 125MS
Language: C++       Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 1024;

bool isPrime[N+1];

void initPrime()
{
    int i, j;
    for (i = 0; i <= N; i ++)
        isPrime[i] = true;
    isPrime[0] = isPrime[1] = false;
    for (i = 2; i <= N; i ++) {
        if (isPrime[i]) {
            for (j = i*i; j <= N; j += i)
                isPrime[j] = false;
        }
    }
}

typedef long long LL;
typedef pair<LL, LL> P;

LL fact(int n)
{
    LL res = 1;
    while (n > 1) {
        res *= n;
        n --;
    }
    return res;
}

P solve(int n)
{
    int i, j;
    queue<int> q;
    for (i = 2; i <= N; i ++) {
        if (!isPrime[i]) continue;
        if (n % i == 0) {
            j = 0;
            while (n % i == 0) {
                n /= i;
                j ++;
            }
            q.push(j);
            if (n == 1) break;
        }
    }
    if (n > 1)
        q.push(1);

    P res = P(0, 1);
    while (q.size()) {
        int m = q.front();
        q.pop();
        res.first += m;
        res.second *= fact(m);
    }
    res.second = fact(res.first) / res.second;
    return res;
}

int main(void)
{
    int n;
    initPrime();
    while (cin >> n) {
        P res = solve(n);
        printf("%lld %lld\n", res.first, res.second);
    }
    return 0;
}

POJ3292

http://poj.org/problem?id=3292

题意

对于4*n+1(n为自然数)组成的集合,乘法在该集合中是封闭的。现规定h-prime是这个集合的数字中只有1和本身能整除的数(不含1)。其余为h合数。从h合数中划分出一部分数字,这些数是由两个h素数相乘得到的,称之为h-semi。现要求在指定1~n范围内有多少个h-semi。

思路

注意这里的素数区别于普通意义上的素数,要根据题目中给出的规则来判定。但求h-prime时仍可用素数筛法,求出h-prime后就可以以两两素数相乘的循环找出所有h-semi。

代码

Source Code

Problem: 3292       User: liangrx06
Memory: 3136K       Time: 16MS
Language: C++       Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 1000001;

bool isPrime[N+1];
bool isSemi[N+1];
int countSemi[N/4+1];

void initPrime()
{   
    int i;
    for (i = 0; i <= N; i ++)
        isPrime[i] = true;
    isPrime[0] = isPrime[1] = false;
    for (i = 5; i <= N; i += 4) {
        if (isPrime[i]) {
            if (i > (int)sqrt((double)N)) continue;
            for (int j = i*i; j <= N; j += 4*i)
                isPrime[j] = false;
        }
    }
}

void initSemi()
{
    int i, j;
    for (i = 1; i <= N; i += 4) {
        isSemi[i] = false;
        countSemi[i/4] = 0;
    }
    for (i = 5; i <= N; i += 4) {
        if (i > (int)sqrt((double)N)) break;
        for (j = i; j <= N; j += 4) {
            if (i * j > N) break;
            if (isPrime[i] && isPrime[j])
                isSemi[i * j] = true;
        }
    }
    for (i = 5; i <= N; i += 4) {
        countSemi[i/4] = countSemi[i/4-1];
        if (isSemi[i])
            countSemi[i/4] ++;
    }
}

int main(void)
{
    int n;
    initPrime();
    initSemi();
    while (cin >> n && n) {
        printf("%d %d\n", n, countSemi[n/4]);
    }
    return 0;
}

POJ3641

http://poj.org/problem?id=3641

题意

判断p是否是基于a的伪素数。输入p,a,两个数如果p是素数输出no,如果p不是素数,判断a^p%p==a是否成立,如果成立输出yes,否则输出no。

思路

这个题在书中分类到快速幂运算里面了,显然分类错误,应该分类到素数
主要考察素数判定,这个题对时间复杂度要求不高,采用i从2到sqrt(n)能否整除n的方式就可以。

代码

Source Code

Problem: 3641       User: liangrx06
Memory: 212K        Time: 16MS
Language: C++       Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

typedef long long LL;

bool isPrime(LL n)
{
    int i;
    for (i = 2; i <= (LL)sqrt(double(n)); i ++) {
        if (n % i == 0)
            return false;
    }
    return true;
}

int main(void)
{
    LL a, p;

    while (cin >> p >> a) {
        if (!p && !a) break;

        if (isPrime(p)) {
            printf("no\n");
            continue;
        }

        LL p0 = p, a0 = a;
        LL res = 1;
        while (p0) {
            if (p0 % 2 == 1)
                res = (res * a0) % p;
            a0 = (a0 * a0) % p;
            p0 /= 2;
        }
        if (res == a)
            printf("yes\n");
        else
            printf("no\n");
    }

    return 0;
}

你可能感兴趣的:(素数,poj,AOJ,挑战程序设计竞赛)