基础数论 习题讲解【C++算法竞赛】

【指指点点】你的题做完了吗

        《要求》 今天听我讲课的学弟学妹们给我点个赞【强制性任务】

        好的那咱们现在开始讲题

请听题!

A.线性筛素数

题目概述

给定一个范围 n,有 q 个询问,每次输出第 k 小的素数。

输入:

第一行包含两个正整数 n,q,分别表示查询的范围和查询的个数。

接下来 q 行每行一个正整数 k,表示查询第 k 小的素数。

输出:

输出 q 行,每行一个正整数表示答案。

解析

        感觉他在题目背景上提示了需要用std::ios::sync_with_stdio(0)来加速,所以理论上大概率是要卡时间的,直接上理论上最快方法欧拉筛。

        本着作死实践的精神,我试了一下cin,确实有一个超时,但这不是更搞笑的,最搞笑的是同样的代码我提交的第二次就过了[我只是想起来第一遍没截图所以才尝试的第二遍]

        最好笑的是我想探究这个原因的时候,我换了一个账号提交,交了四次,都AC了,这好像在嘲笑我第一遍交的手气就是个非酋????今天手气太差了抽卡只有小保底,1:100的时间限制也被我刷出来了

        基础数论 习题讲解【C++算法竞赛】_第1张图片【汗流浃背了】这并不鼓励大家比赛的时候去赌这个概率

代码

#include 
#include
#include 
using namespace std;

bool visit[100000010];//1表示素数,根据数据范围,这个设的尽可能大n<=1e8
int Prime[100000010];
int sum = 0;//计数

void E(int n)
{
	memset(visit, 1, sizeof(visit));//初始状态全部设为1,即全部都是素数
	visit[1] = 0;//1不是素数

	for (int i = 2; i <= n; i++)
	{
		if (visit[i])
			Prime[++sum] = i; 

		for (int j = 1; j <= sum&& i * Prime[j] <= n; j++)//i * Prime[j] <= n;不越界/不多算
		{
			visit[i * Prime[j]] = 0;
			if (i % Prime[j] == 0)//欧拉筛的关键判断及其跳出
				break; 
		}
	}
}

int main()
{
	ios::sync_with_stdio(0);//加这个是为了加速,当时我没加也过了
	int n, q;
	cin >> n >> q;
	E(n);//一次运算 全部标注
	while (q--)
	{
		int k;
		cin>>k;
		cout<

        纯属模板题,不会写的去给我背模板基础数论 习题讲解【C++算法竞赛】_第2张图片

        讲完课的第八十分钟,只有十五个人写出来了o(TヘTo)

B.数字游戏

题目概述:

基础数论 习题讲解【C++算法竞赛】_第3张图片

基础数论 习题讲解【C++算法竞赛】_第4张图片

解析

翻译一下:

        上帝给出数字Q,K为先手,以Q的因数代替Q,再由C操作,规定没有数字写写出的玩家胜利

        K:6->2 && 3,用3代替或者是用2代替本质上是一样的,因为都是素数

        C:2->选0,C胜利

        本质上是博弈论+求素数

        所以这道题本质上针对Q的输入,分为三种情况:

        1、Q为质数【若是第一次就无法写出数字,则认为第一次写出的可以制胜的最小数字为 0】这种情况下输出0

        2、Q为两个质数的乘积=Q仅有两个因子,这样先手方必败

        3、Q为多个(n>2)个指数的乘积=Q有多个因子,那我们努力让先手(我方)在状态2的情况下,让后手(对方)得到状态2的情况,这样才是我放的必胜策略

注意!基础数论 习题讲解【C++算法竞赛】_第5张图片

        所以需要开long long

代码

#include
#include
using namespace std;

int main()
{
	long long a[1000005];
	long long ans = 0;
	long long Q;
	cin >> Q;
	for (long long i = 2; i * i <= Q; i++)
		while (Q % i == 0)//纯暴力分解质因子,貌似对速度要求不大也,毕竟不是循环输入,而是单次的
		{
			a[++ans] = i;
			Q /= i;
		}
	if (Q != 1)  
		a[++ans] = Q;
	if (ans == 1)
	{
		cout << "1" << endl;
		cout << 0 << endl;
	}
	else if (ans == 2) 
		cout << "2";
	else 
		cout << "1" << endl << a[1] * a[2];
	return 0;
}

 C.组合数问题

题目概述:

基础数论 习题讲解【C++算法竞赛】_第6张图片

解析

        当我拿到这个题的时候汗流浃背了,上课忘记讲杨辉三角了基础数论 习题讲解【C++算法竞赛】_第7张图片

        不慌,不会杨辉三角也能过

杨辉三角

基础数论 习题讲解【C++算法竞赛】_第8张图片

杨辉三角的输出:

  //使第一列和对角线元素的值为1
  for (i=1;i

        所以在本题中,c(i,j)的值就是第i行第j列

代码

#include 
#include 
using namespace std;    
int t, k, n, m;
int C[2005][2005], s[2005][2005];
void Y()
{
    C[1][1] = 1;
    for (int i = 0; i <= 2000; i++) 
        C[i][0] = 1;
    for (int i = 2; i <= 2000; i++) 
    {
        for (int j = 1; j <= i; j++) 
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % k;
    }
    for (int i = 2; i <= 2000; i++) 
    {
        for (int j = 1; j <= i; j++) 
        {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
            if (C[i][j] == 0) 
                s[i][j] += 1;
        }
        s[i][i + 1] = s[i][i];
    }
}
int main() 
{
    memset(C, 0, sizeof(C));
    memset(s, 0, sizeof(s));
    cin >> t >> k;
    Y();
    while (t--) 
    {
        cin >> n >> m;
        if (m > n) m = n;
        cout << s[n][m] << endl;
    }
    return 0;
}

        这个题第一遍提交的时候CE了,居然不算惩罚时长【可喜可贺】

D.排列排序

        这就是一道排序,和排列数和今天上午讲的课没有任何关系

题目概述

基础数论 习题讲解【C++算法竞赛】_第9张图片

解析:

        区间是变动的,换句话说,区间两端都是变动的,所以可以分别指向区间两端……设置两个数i和j,i指向区间左端点,j指向区间右端点,然后开始进行枚举。

        这里有一个让题目变得简单的条件,就是说【直到 p 中元素的值从 1 到 n 按升序排序】,也就是说,在正常排序的情况下,这个位置的下标值应该和元素值相同在。换句话说,元素值与下标值相同就代表元素处于正确的位置,那么i+=1;

        那么元素值与下标值不同的时候,就代表着此时该元素处于不正确的位置上,此时i指向的数字,作为排序区间的左端点,此时右指针初始化为j=i+1,开始进行j++直到【判断_此刻>=区间最大值】①,此时j作为排序的右端点。此时区间长度j-i+1

        之后再有 i=j+1重复上述过程

①为啥是区间最大值呢?

        比如区间是[2,4],长度为3,该区间内有一个数字5,是区间最大值,此刻区间右端点(4)还没有大于等于5,此刻区间还没有到最大值对应的位置,需要更进一步,让区间右端点再往后移动。

代码:

#include 
#include 
using namespace std;

int a[1000005];

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        int sum = 0;
        cin >> n;
        for (int i = 1; i <= n; i++) 
            cin >> a[i];
        int i = 1;
        while (i <= n)
        {
            if (a[i] == i) 
                i++;
            else 
            {
                int mmax = a[i];
                int j = i + 1;
                mmax = max(mmax, a[j]);
                while (mmax > j)
                {
                    j++;
                    mmax = max(mmax, a[j]);
                }
                sum += j - i + 1;
                i = j + 1;
            }
        }
        cout << sum << endl;
    }
    return 0;
}

E.最小公倍数之和

题目概述

基础数论 习题讲解【C++算法竞赛】_第10张图片

解析:

基础数论 习题讲解【C++算法竞赛】_第11张图片……今下午我和你们一块做的题,结果被一杀,是我低估,非常佩服拿了这道题一血的那位同学,感兴趣的话可以分享一下你的代码,如果你愿意的话我可以贴在这道题下面。同时欢迎所有做出来任意一道题的同学,都可以投稿你的想法。

        当我被WA的时候我震惊了,质疑自己,谁能想到呢自以为看懂了题目

        仔细看看,更加完蛋,没讲莫比乌斯反演基础数论 习题讲解【C++算法竞赛】_第12张图片那这道题就有点尴尬了,我们简单说说,毕竟莫比乌斯八百年不考一次,天梯赛肯定不考,大家可以跳过,感兴趣的同学可以继续看看。

        最后是为了防止你们提前AK掉

基础数论 习题讲解【C++算法竞赛】_第13张图片

基础数论 习题讲解【C++算法竞赛】_第14张图片

       整体来讲先枚举公因数做一下预处理,然后求解∑u(x)*x,最后暴力一下

        只能说这题是纯数论,确实有很多知识点是咱们没有学过的,能看得懂就看懂了,看不懂就算了去做别的题吧,一般来讲国内性质的比赛很少比这样难的。咱鼓励不会的题该放弃放弃

代码

#include
#include
#include
#include
using namespace std;
const long long maxn = 50010;
long long n, A[maxn], m, a[maxn], f[maxn], ans, tot;
long long cur, pri[maxn], mu[maxn];
bool tf[maxn];
main()
{
    cin >> n;
    for (long long i = 1; i <= n; i++)
    {
        scanf("%lld", A + i);
        a[A[i]]++;
        m = 50000;
    }
    for (long long i = 1; i <= m; i++)
        for (long long j = i; j <= m; j += i)
            f[i] += a[j] * j;
    for (long long i = 1; i <= m; i++)
        f[i] = f[i] * f[i];
    mu[1] = 1;
    for (long long i = 2; i <= m; i++)
    {
        if (!tf[i]) 
        {
            pri[++cur] = i; 
            mu[i] = -1;
        }
        for (long long j = 1; j <= cur && pri[j] <= m / i; j++)
        {
            tf[i * pri[j]] = true;
            if (i % pri[j] == 0) 
            { 
                mu[i * pri[j]] = 0; 
                break; 
            }
            else 
                mu[i * pri[j]] = -mu[i];
        }
    }
    for (long long d = 1; d <= m; d++)
    {
        tot = 0;
        for (long long g = 1; g <= m / d; g++)tot += mu[g] * f[g * d];
        ans += tot / d;
    }
    cout << ans;
    return 0;
}

小结

最后总结一下,本次题目里面:

第一题是模板题,一小时内做出来才算是及格线【勉强】

第二题是博弈论,很大程度上是一个数学敏感度,这个确实是有灵感这么一说,所以当作长经验值了。

第三题比较难但还在可以接受的范围内,大家熟悉一下杨辉三角,基本的推导公式:【每个数等于它上方两数之和】可能会出现考题or找规律的题找出来就是杨辉三角

第四题也比较简单,一个思维上的变通?常规题目了算是,应该做出来的。如果觉得自己肯定作对但是最后WA的可以看一下是不是数组没有开的足够大或者没有开对

第五题属于不用看了,熟悉一下最大公因数和最小公倍数的计算代码吧

and then 大家还可以看一下今天ppt里面提到的题目,更贴合今天的讲课内容。

最后给一下求最小公约数的代码(完整版)虽然和今天的题没有一点关系

#include
#include
using namespace std;

//求最大公约数
int gcb(int a, int b)
{
    int c = 0;
    while (c = a % b)
    {
        a = b;
        b = c;
    }
    return b;
}

int lcm(int a, int b)
{
    return a * b / gcb(a, b);
}

你可能感兴趣的:(C++算法竞赛,算法,c++,数据结构)