数论知识练习

数论练习

几个数论入门题的练习(看y总推荐的几个简单的,就拿来练习试试看)

POJ 1365 Prime Land

题目:
在这里插入图片描述
题目翻译:
太长了,不知道具体是啥
大致就是每一行输入一堆数,前面一个是质数本身,后面一个是他的指数,最后把所有的数相乘就是x,然后求对x-1的一个序列,序列表示含义和输入的一样(前面一个数是一个质数,后面是他的指数),质数从大到小排序输出。

思路:水题,数据读进去,然后直接唯一分解定理。

代码:

/*POJ 1365*/

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <string>
#include <math.h>

using namespace std;

#define int long long
#define endl '\n'

vector<int> prime;
bool isPrime[32780];

//线性筛素数
void getPrimes()
{
    int n = 32767;
    for (int i = 2; i <= n; i++)
    {
        if (!isPrime[i])
        {
            prime.push_back(i);
            isPrime[i] = true;
        }
        int m = prime.size();
        for (int j = 0; i * prime[j] <= n && j < m; j++)
        {
            isPrime[i * prime[j]] = true;
            if (i % prime[j] == 0)
                break;
        }
    }
}

//从行里面得到数字
int getNum(string s)
{
    int n = s.size();
    int res = 1;
    int tmp = 0;

    int cnt = 0;
    int a, b;
    for (int i = 0; i < n; i++)
    {
        if (s[i] != ' ')
            tmp = tmp * 10 + s[i] - '0';
        else
        {

            if (cnt == 1)
            {
                b = tmp;
                res = res * pow(a, b);

                cnt = 0;
            }
            else if (cnt == 0)
            {
                cnt++;
                a = tmp;
            }
            tmp = 0;
        }
    }
    res = res * pow(a, tmp);
    return res;
}

signed main()
{

    getPrimes();

    while (true)
    {
        string s;

        getline(cin, s);
        if (s.size() == 1 && s[0] == '0')
            break;

        int num = getNum(s) - 1;

        map<int, int, greater<int>> mp;

        while (num) //唯一分解定理
        {
            int n = prime.size();
            for (int i = n - 1; i >= 0; i--)
            {
                while (num % prime[i] == 0)
                {
                    mp[prime[i]]++;
                    num /= prime[i];
                }
                if (num == 1)
                    break;
            }
            if (num == 1)
                break;
        }

        map<int, int>::iterator it;
        for (it = mp.begin(); it != mp.end(); it++)
            cout << it->first << " " << it->second << " ";

        cout << endl;
    }

    return 0;
}

POJ 2034 Anti-prime Sequences

题目:
在这里插入图片描述
题目翻译:
对n ~ m连续的数进行排列,找到最小的一组排列,使得数组中,相邻不大于d位的和为合数。

思路:先筛出所有的素数,然后dfs爆搜。

需要注意的地方:
1、素数数组的大小应该开到10000,n最大1000,d最大10,1000*10。
2、判断的时候,倒着判断,直接累加。
比较简单的一道题,因为数组开太小了T了无数次QAQ。

代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
#define endl "\n"

const int max_n = 1010;

vector<int> primes;
vector<int> res;
bool isPrime[max_n * 10];
bool vis[max_n];

//筛素数
void getPrimes()
{
    for (int i = 2; i <= max_n * 10; i++)
    {
        if (!isPrime[i])
            primes.push_back(i);

        int m = primes.size();
        for (int j = 0; i * primes[j] <= max_n * 10 && j < m; j++)
        {
            isPrime[i * primes[j]] = true;
            if (i % primes[j] == 0)
                break;
        }
    }

    memset(isPrime, 0, sizeof(isPrime));
    int n = primes.size();
    for (int i = 0; i < n; i++)
        isPrime[primes[i]] = true;
}

//判断
bool isok(int d, int x, int index)
{
    if (index == 0)
        return true;

    int sum = x;
    int tmp = 0;

    for (int i = index - 1; i >= 0; i--)
    {
        sum += res[i];
        tmp = i;
        break;
    }

    for (int j = 2; j <= d; j++)
    {
        if (isPrime[sum])
            return false;
        if (tmp > 0)
        {
            sum += res[tmp - 1];
            tmp--;
        }
    }
    return true;
}

bool dfs(int n, int m, int d, int index)
{
    if (index == m - n + 1)
        return true;
    for (int i = n; i <= m; i++)
    {
        if (!vis[i] && isok(d, i, index))
        {
            res[index] = i;
            vis[i] = true;
            if (dfs(n, m, d, index + 1))
                return true;
            vis[i] = false;
        }
    }
    return false;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    getPrimes();
    int n, m, d;

    while (cin >> n >> m >> d)
    {
        if (n == 0 && m == 0 && d == 0)
            break;

        memset(vis, 0, sizeof(vis));
        res.resize(m - n + 1);
        if (dfs(n, m, d, 0))
        {
            int k = res.size();
            for (int i = 0; i < k; i++)
            {
                cout << res[i];
                if (i != k - 1)
                    cout << ",";
            }
            cout << endl;
        }
        else
            cout << "No anti-prime sequence exists." << endl;
    }
    return 0;
}

POJ 2407 Relatives

题目:
在这里插入图片描述
题目翻译:
求n以内与n互质的数的个数。

思路:欧拉函数。需要注意的就是数据范围1e9。

代码:

#include <iostream>

using namespace std;

int euler(int n)
{
    int rea = n;
    for (int i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
        {
            rea = rea - rea / i;

            while (n % i == 0)
                n /= i;
        }
    }
    if (n > 1) //最后一个素数
        rea = rea - rea / n;
    return rea;
}

int main()
{
    int n;
    while (cin >> n)
    {
        if (n == 0)
            break;
        cout << euler(n) << endl;
    }
    return 0;
}

POJ 2689 Prime Distance

题目:
在这里插入图片描述
题目翻译:
输入一个l和一个r,寻找区间中所有相邻素数的差的最小值和最大值。

思路:因为数据范围特别大,所以使用区间筛素数。

代码

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int max_n = 1e6 + 10;

bool isPrime[max_n];
vector<int> primes1;
bool isnPrimes[max_n];

//区间筛素数
void getPrimes(int l, int r)
{
    for (int i = 2; i <= max_n; i++)
    {
        if (!isPrime[i])
        {
            primes1.push_back(i);
            for (int j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i)
                isnPrimes[j - l] = true;
        }
        isPrime[i] = true;
        int m = primes1.size();

        for (int j = 0; j < m && i * primes1[j] <= max_n; j++)
        {
            isPrime[i * primes1[j]] = true;
            if (i % primes1[j] == 0)
                break;
        }
    }
}

signed main()
{
    int l, r;

    while (cin >> l >> r)
    {
        primes1.resize(0);
        fill(isnPrimes, isnPrimes + max_n, false);
        fill(isPrime, isPrime + max_n, false);

        getPrimes(l, r);

        vector<int> primes;
        if (l == 1)
            isnPrimes[0] = true;
        for (int i = l; i <= r; i++)
        {
            if (!isnPrimes[i - l])
                primes.push_back(i);
        }

        int n = primes.size();
        int mi1, mi2, mi3 = 1 << 30;
        int ma1, ma2, ma3 = 0;

        for (int i = 1; i < n; i++)
        {
            if (primes[i] - primes[i - 1] > ma3)
            {
                ma1 = primes[i - 1];
                ma2 = primes[i];
                ma3 = ma2 - ma1;
            }
            if (primes[i] - primes[i - 1] < mi3)
            {
                mi1 = primes[i - 1];
                mi2 = primes[i];
                mi3 = mi2 - mi1;
            }
        }

        if (ma3 != 0)
            cout << mi1 << "," << mi2 << " are closest, " << ma1 << "," << ma2 << " are most distant." << endl;
        else
            cout << "There are no adjacent primes." << endl;
    }
    return 0;
}

POJ 2739 Sum of Consecutive Prime Numbers

题目:
在这里插入图片描述
题目翻译:
输入一个数,使用n个连续的素数将它表示出来,每有一种表示方法,结果加1。

思路:水题。打表暴力。

代码:

#include <iostream>
#include <vector>
using namespace std;

const int max_n = 1e4 + 10;
vector<int> primes;
bool isPrime[max_n];
void getPrimes()
{
    for (int i = 2; i <= max_n; i++)
    {
        if (!isPrime[i])
            primes.push_back(i);
        int m = primes.size();
        for (int j = 0; j < m && i * primes[j] <= max_n; j++)
        {
            isPrime[i * primes[j]] = true;
            if (i % primes[j] == 0)
                break;
        }
    }
}

int main()
{
    getPrimes();
    int n;
    while (cin >> n)
    {
        if (n == 0)
            break;
        int m = primes.size();

        int cnt = 0;
        for (int i = 0; i < m; i++)
        {
            int sum = 0;
            for (int j = i; j < m; j++)
            {
                sum += primes[j];
                if (sum == n)
                {
                    cnt++;
                    break;
                }
                else if (sum > n)
                    break;
            }
        }
        cout << cnt << endl;
    }
    return 0;
}

POJ 3126 Prime Path

在这里插入图片描述
题意:
输入两个四位数,每次只能改变一个数字,要求从第一个数变成第二个数最小步骤是多少,中间可以变得数只能是质数。

思路:筛素数然后bfs。

注意的地方:宽搜的时候去枚举每一位数字,判断那个数是否是素数,不要直接去枚举素数。直接枚举素数会T的亲妈都不认得。(感觉正常人应该都是这么写的,不知道自己刚开始为什么会直接去枚举素数

代码:

#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <set>

using namespace std;
const int max_n = 1e4;
#define endl "\n"

vector<int> primes;
bool isPrime[max_n];

struct STATE
{
    int num;
    int step;
    STATE(){};
    STATE(int _num, int _step) { num = _num, step = _step; };
};

void getPrimes()
{
    for (int i = 2; i <= max_n; i++)
    {
        if (!isPrime[i])
            primes.push_back(i);
        int m = primes.size();
        for (int j = 0; j < m && i * primes[j] <= max_n; j++)
        {
            isPrime[i * primes[j]] = true;
            if (i % primes[j] == 0)
                break;
        }
    }
}

int bfs(int st, int ed)
{
    set<int> sat;
    STATE t(st, 0);
    queue<STATE> q;
    q.push(t);

    while (q.size())
    {
        STATE s = q.front();
        q.pop();

        if (s.num == ed)
            return s.step;

        if (sat.count(s.num))
            continue;
        sat.insert(s.num);

        //对每一位进行枚举
        for (int i = 0; i <= 9; i++)
        {
            int tmp = s.num / 10 * 10 + i;
            if (!sat.count(tmp) && !isPrime[tmp])
            {
                STATE t1(tmp, s.step + 1);
                q.push(t1);
            }

            tmp = s.num / 100 * 100 + i * 10 + s.num % 10;
            if (!sat.count(tmp) && !isPrime[tmp])
            {
                STATE t1(tmp, s.step + 1);
                q.push(t1);
            }

            tmp = s.num / 1000 * 1000 + i * 100 + s.num % 100;
            if (!sat.count(tmp) && !isPrime[tmp])
            {
                STATE t1(tmp, s.step + 1);
                q.push(t1);
            }

            if (i == 0)
                continue;
            tmp = i * 1000 + s.num % 1000;
            if (!sat.count(tmp) && !isPrime[tmp])
            {
                STATE t1(tmp, s.step + 1);
                q.push(t1);
            }
            //cout << tmp << " " << endl;
        }
    }
    return -1;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    getPrimes();

    int t;
    cin >> t;
    while (t--)
    {
        int st, ed;
        cin >> st >> ed;

        int res = bfs(st, ed);
        if (res == -1)
            cout << "Impossible" << endl;
        else
            cout << res << endl;
    }

    return 0;
}

POJ 3518 Prime Gap

在这里插入图片描述
题意:
输入一个n,输出包含n的最长连续合数的个数。如果n是质数或者没有,就输出0。
思路:线性筛,然后枚举输出,大水题。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int max_n = 1299709 + 10;

vector<int> primes;
bool isPrime[max_n];
void getPrimes()
{
    for (int i = 2; i < max_n; i++)
    {
        if (!isPrime[i])
            primes.push_back(i);
        int m = primes.size();
        for (int j = 0; j < m && primes[j] * i < max_n; j++)
        {
            isPrime[i * primes[j]] = true;
            if (i % primes[j] == 0)
                break;
        }
    }
}

int main()
{
    getPrimes();

    int n;
    while (cin >> n)
    {
        bool isok = false;
        if (n == 0)
            break;
        int m = primes.size();
        for (int i = 0; i < m - 1; i++)
        {
            if (primes[i] < n && primes[i + 1] > n)
            {
                isok = true;
                cout << primes[i + 1] - primes[i] << endl;
                break;
            }
        }
        if (!isok)
            cout << "0" << endl;
    }
    return 0;
}

总结

题才写了3个就开始总结了就离谱
以后做到题了再补上。
应该听学长的话,难的不会,简单的不出(

题目写完了,这次写的都是一些简单的题,重点知识就是线性筛和欧拉函数了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值