2020级cpp机考模拟题A卷-#题解2

 

这部分的题目都有一定难度,有兴趣的同学可以钻研一下。

特此感谢来自BDT20030  tql的支持。


2:素数的和-2

题意:

计算不大于m的素数之和。(多么容易理解的题目啊,对吧)

题解(有点复杂的算法实现):

这题的难度就在于如何在不超时的情况下完成计算。(ps:内存上没卡你,不然更痛苦)

请直接看代码,注释打了真的很久,把30号的超纲代码改得不超纲还能绿也用了好久。

上板子(埃氏筛,稍易):

#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<stdlib.h>
using namespace std;
const int M = 10000005, mod = 100000007;//因为m的最大值是10000000,定义一个常量稍大于m就可以了
bool b[M];//都定义为全局变量,可以在主函数里调用
int p[M], cnt = 0;//p用于存储m以内的全部素数,cnt为个数
void k()
{
    for (int i = 2; i <= M; i++)//1不是素数,所以b[1]=0.
    {
        b[i] = 1;//先当他们全是素数,下面再用循环判断
    }
    for (int i = 2; i < sqrt(M); i++)//判断M以内的各个数字是否为素数
    {
        if (b[i])
        {
            cnt++;//用于记录p中有几个数据
            p[cnt] = i;//p[ ]用于存储各个<=M的素数
            for (int j = i*i; j < M; j+=i)//一定要判断j<=m,保证数组不会爆
            {
                b[j] = 0;//i 存储的是素数,j 为某素数的倍数,这个数一定不是素数
            }
        }
    }
}
int main()
{
    k();//因为前面是全局变量所以使用函数后面能够调用
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int m;
        long long int sum = 0;
        cin >> m;
        for (int i = 1; i <= cnt; i++)
        {
            if (p[i] <= m)
                sum = (sum + p[i]) % mod;//素数累加就好了,大家都会
        }
        cout << sum << endl;
    }
    return 0;
}

 

上板子(线性筛,较难):

#include<iostream>
#include<cmath>
using namespace std;
const int M = 10000005, mod = 100000007;//因为m的最大值是10000000,定义一个常量稍大于m就可以了
bool b[M];//都定义为全局变量,可以在主函数里调用
int p[M], cnt = 0;//p用于存储m以内的全部素数,cnt为个数
void k()
{
    for (int i = 2;i <= M;i++)//1不是素数,所以b[1]=0.
    {
        b[i] = 1;//先当他们全是素数,下面再用循环判断
    }
    for (int i = 2;i <= M;i++)//判断M以内的各个数字是否为素数
    {
        if (b[i])//b[ ]用于判断各个数字是否为素数,是则=1,不是=0
        {
            cnt++;//用于记录p中有几个数据
            p[cnt] = i;//p[ ]用于存储各个<=M的素数
        }
        for (int j = 1;j <= cnt && i * p[j] <= M;j++)//一定要判断i*p[j]<=m,保证数组不会爆
        {
            b[i * p[j]] = 0;//p[j]存储的是素数,i*p[j]为某素数的倍数,这个数一定不是素数(这是为了减少循环次数,缩短时间)
            if (i % p[j] == 0)break;//假如i是某个素数的倍数,就不用进行循环了(这也是为了缩短时间)
        }
    }
}
int main()
{
    k();//因为前面是全局变量所以使用函数后面能够调用
    int n;
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        int m;
        long long int sum = 0;
        cin >> m;
        for (int i = 1;i <= cnt;i++)
        {
            if(p[i]<=m)
            sum = (sum + p[i]) % mod;//素数累加就好了,大家都会
        }
        cout << sum << endl;
    }
    return 0;
} 

5:不喜欢的数字

题意:

计算1到m中不属于p1/p2/p3倍数的数的个数并输出。

难度1:内存只给了 2048 KB;难度2:时间卡在了1s;难度3:m的范围达到了e9(仍在int范围内)。

所以不要尝试去开数组了!(我真是个傻子,看到e9还尝试用bool型开数组)

题解:

首先,复习一下辗转相除法求最大公因数:

int gcd(int a, int b)//求a、b的最大公因数
{
    if (a < b)
        swap(a, b);
    int c;
    while (a % b != 0)
    {
        c = a % b;
        a = b;
        b = c;
    }
    return b;
}

假设范围是1-m,给出p1,要求该范围内p1倍数的个数,则有number=m/p1。所以要找出1-m范围内p1、p2、p3的倍数个数,只要先算出p1、p2、p3在该范围内的倍数的个数(然后再相加就行了,才怪),然后要算出重复的数字(即公倍数)的个数,减去他们被重复计算的次数,这样才能得到正确的个数。

 

对于e9,其实用long int会好一点,毕竟e9的话距离int极限也差不多了,加个long的话可以不去担心他在四则运算中随随便便就炸了//当然有时候你用了long long也还是会炸。

上板子:

int gcd(int a, int b)//求a、b的最大公因数
{
    if (a < b)
        swap(a, b);
    int c;
    while (a % b != 0)
    {
        c = a % b;
        a = b;
        b = c;
    }
    return b;
}
int lcm(int a, int b)//求两个数的最小公倍数
{
    return a * b / gcd(a, b);
}
int lcm(int a, int b,int c)//求三个数的最小公倍数
{
    return lcm(lcm(a,b),c);
}
int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        int m, p1, p2, p3,a;
        cin >> m >> p1 >> p2 >> p3;
        int a1 = m / p1, a2 = m / p2, a3 = m / p3;// a1/a2/a3的倍数的个数
        int a12 = m / lcm(p1, p2), a23 = m / lcm(p2, p3), a13 = m / lcm(p1, p3);// a1、a2、a3两两数的最小公倍数的倍数的个数
        int a123 = m / lcm(p1, p2, p3);//a1、a2、a3最小公倍数的个数
        a = a1 + a2 + a3 - a12 - a13 - a23 + a123;//通过加减删去重复计算的数,得到p、p2、p3的倍数的个数
        if (p1 == 1 || p2 == 1 || p3 == 1)a = m;
        cout <<m - a << endl;
    }
    return 0;
}

 

 

正在更新中......

制作:BDT20040

posted @ 2020-12-21 21:42  流白李  阅读(216)  评论(0编辑  收藏  举报