国庆NOIP储备营讲课笔记

发布时间 2023-09-29 13:43:40作者: FinderHT

Day1(基础算法)

讲师:余快

枚举法

例题1

给定一个数 \(x\),判断 \(x\) 是不是质数。

朴素算法:枚举 \([2,x−1]\) 之间所有的整数 \(i\),逐个判断 \(x\) 是否被 \(i\) 整除,若都不能整除则 \(x\) 是质数,时间复杂度 \(O(x)\),搞个 \(10^9\) 直接卡过。该怎么优化呢?

优化枚举范围:只需枚举到 \(\sqrt{x}\) 即可

例题2

洛谷 P1149 [NOIP2008提高组]火柴棒等式

简要思路

for 循环 + check 判断。
首先枚举计算所有的数所需要的火柴数量(设立数组 \(g\)\(g_i\) 代表 i 这个数所需要的火柴数)。

例题3

洛谷 P1115 最大子段和

暴力枚举(\(0\text {pts}\)):
枚举区间的左端点,右端点,然后从左端点开始到右端点逐个加起来。
时间复杂度 \(O(n^3)\)

前缀和优化的朴素算法(\(40\text {pts}\)):
先处理 \(s_i=a_1+a_2+ \dots +a_i\)
枚举区间的左端点 \(i\),右端点 \(j\),这段区间的和就是\(s_j − s_{i−1}\)
时间复杂度 \(O(n^2)\)

优化枚举范围(\(100\text {pts}\)):
如果我们固定右端点 \(i\) ,那么我们选择的左端点 \(j\) 一定满足 \(s_{j −1}\) 最小,这样的 \(s_i−s_{j−1}\) 是最大的。
于是我们从左到右枚举右端点 \(i\),维护 \([1,i]\) 里满足 \(s_{j−1}\) 最小的 \(j\),然后直接选择 \(j\) 作为左端点。
时间复杂度 \(O(n)\)

悄悄说一下,其实这道题正解是 dp。

例题4

P1638 逛画展

模拟算法

模拟就是用计算机来模拟题目中要求的操作。

模拟题目通常具有码量大、操作多、思路繁复的特点。由于它码量大,经常会出现难以查错的情况,如果在考试中写错是相当浪费时间的。

例题1

洛谷 P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布

例题2

洛谷 P1563 [NOIP2016 提高组] 玩具谜题

技巧

写模拟题时,遵循以下的建议有可能会提升做题速度:
在动手写代码之前,在草纸上尽可能地写好要实现的流程。
在代码中,尽量把每个部分模块化,写成函数、结构体或类。
对于一些可能重复用到的概念,可以统一转化,方便处理。
调试时分块调试。模块化的好处就是可以方便的单独调某一部分。
写代码的时候一定要思路清晰,不要想到什么写什么,要按照落在纸上的步骤写。
实际上,上述步骤在解决其它类型的题目时也是很有帮助的。

习题

P1042 [NOIP2003 普及组] 乒乓球
P2670 [NOIP2015 普及组] 扫雷游戏
P3952 [NOIP2017 提高组] 时间复杂度

二分法

定义

二分的定义:在一个单调的有限数列上快速查找某一特定值的方法。

例题1

给你一个单调递增的数组,给你一个数x,问 $ > x, \ge x$的第一个数分别是什么?

可以用 STL 中的 lower_boundupper_bound 轻松解决,不过在这里不讲。

事实上有不同二分的实现方法,刚刚接触二分的OIer经常在这个地方写错,进入死循环

//找第一个>q
int l=1,r=n;
while(l<r){
	int mid=(l+r)>>1;
	if (a[mid]<=q)l=mid+1;
	else r=mid;
}
std::cout<<r;
//找第一个>=q
int l=1,r=n;
while(l<r){
	int mid=(l+r)>>1;
	if (a[mid]>=q)r=mid;
	else l=mid+1;
}
std::cout<<r;

其实我们有最保守的写法
就是l=mid+1;r=mid-1
而答案直接考虑mid

例题2

求一个正整数X开三次根后的结果,保留六位小数。

因为我们这里要二分实数,所以

解法1:
直接输出 pow(x,1/3)

假设我们不知道 pow 函数,只知道二分和 sqrt 函数

01分数规划

分治

快速幂

CSP-S2023第一轮 著名题目:

现在用如下程序来计算x^n,其时间复杂度为( )。

Quick_power(x,k):
	if k==1 return x;
	else return Quick_power(x,k/2)*Quick_power(x,k/2)*(k&1)?x:1;

A.O(n)
B.O(1)
C.O(log n)
D.O(n log n)

答案选A

我们在六年级的时候学过同底数幂的乘法法则,得知 \(x^a \times x^b = x^{a + b}\),因此 \(x^{\frac{y}{2}} \times x^{\frac{y}{2}} = x ^ y\),所以我们可以分治,直到指数为 \(0\)。记当前的结果为 \(z\),底数为 \(x\),指数为 \(y\),按照 \(z^y = z^{\frac{y}{2}} \times z^{\frac{y}{2}}\) 来计算。如果当前递归到的 \(y\) 为偶数,直接返回计算的结果;如果 \(y\) 为奇数,就要把刚才得到的结果再乘上一个 \(x\),再返回。

归并排序

逆序对