郑州集训DAY1笔记

来源:互联网 发布:淘宝卖家遇到诈骗案例 编辑:程序博客网 时间:2024/03/28 20:05

day1难度测试

如果要用一个词来形容上午的测试,那真是体无完肤。
成绩:

题目 成绩 评价 T1 50 一般 T2 10 大失所望 T3 0 差

基础算法

递推

:指通过观察、归纳,发现较大规模问题和较小规模问题之间的关系,用一些数学公式表达出来,在一些教材中,也称为计数DP。递推的模型最主要有:斐波那契数列,卡特兰数,bell数,错排等等。

递归

:所谓递归,是指函数“自己调用自己”的一种编程方法,在解决一个问题时,如果发现问题能拆解为一个或多个相同规模的子问题时,就可以考虑使用这种方法求解。最经典的题型有汉诺塔等。

分治

:分治解决问题的一般步骤:
分,将问题划分为若干个规模较小、形式相同的子问题
治,递归求解,当问题规模足够小时直接求答案
合,将规模更小的问题得到的答案合并起来,得到原问题的答案。
主要模型应用有归并排序、快速幂等。
是不是很难懂?呐看一个例题
分治经典例题:给定 2^n * 2^n 的棋盘,其中有一个坏点,需要用若干个 L 型方块覆盖棋盘:不能覆盖到坏点,其他点要被覆盖恰好一次。给出方案。
解:这道题目有个基本单元一定反复存在:
这里写图片描述
这个只需要放一个L形状的方块去覆盖就解决了问题。我们把它拓展一下,把每个格子想成多个格子的集合,如果我们能确定这个坏点在其中一个集合里面,那么我们可以把整个问题分成四个部分:
这里写图片描述
然后发现左下角的格子就是一个基本模型,然后中间的四块连在一起也是一个模型,然后我们可以在中间搞事情,放上一个L形状的木块,由于每个格子只能恰好被覆盖一次,所以我们可以把所有被覆盖的地方都标记为坏点。然后另外三个大格子也变成了三个模型
这里写图片描述
是不是就做出来了。
二分:二分其实是分治的一种。二分是竞赛中一个非常实用的算法思想通过二分,把最优化问题转化为判定性问题,把优化目标转化为判定的标准,就能发现问题的一些“贪心性质”,从而设计高效算法进行判定。
求“ XXX 的最大值 ” 最小可能是多少(或者反过来)都可以考虑二分
有些时候,二分的模型比较隐蔽,需要更深入的思考和发现性质
二分答案的时间复杂度是O(logn)

倍增

:倍增字面上意思是:成倍地增加。当我们模拟一个过程时,一步一步进行太慢,考虑把模拟的步数二进制分解;经过一些预处理,每次可以模拟 2^i 步,从而达到优化复杂度的目的。主要模型有RMQ,LCA等。

贪心

:哈哈哈哈哈哈贪心是个好东西啊。由贪心得到局部最优解,从而得到全局最优解,一般容易实现、时间复杂度优秀,但是……难于证明,贪心的脑洞一般都很大。

数论

特特特特特特就是个数论。

模算术

模算术可以这样简单理解:就是不停地模啊模。就可以了。比如a+b=c(mod p)就代表(a+b) mod p=c mod p;模算术只有在模意义下才有固定的意义。

乘法逆元

类比倒数,如果a*b=1,那么ab互为倒数;类似地,我们定义如果在 mod p意义下,a *b=1,那么a和b互为mod p意义下的乘法逆元。

bezout 定理

方程 ax + by = c 有解,当且仅当 gcd(a, b) | c。

欧几里得算法

gcd(a, b) = gcd(a - b, b)
事实上,定理直接给出了一种计算 gcd 的方式,直接根据上述公式计算,复杂度 O(\log min(a, b))

扩展欧几里得算法

事实上,我们可以直接在欧几里德算法求解 gcd(a, b) 的过程中,构造一组 ax + by = gcd(a, b) 的解
这个方法依赖于递归的思想
边界:b = 0 时, a * 1 + b * 0 = gcd(a, b)
设我们找到了一组 bx + (a % b)y = gcd(a, b) 的解,那么:
bx + (a - [a / b] * b]) y = gcd(a, b) ==>
ay + b * (x - [a / b] * y) = gcd(a, b)
令 x’ = y, y’ = (x - [a / b] * y),可以得到:
ax’ + by’ = gcd(a, b)

二元一次不定方程的通解

令 d = gcd(a, b)
当我们求出 ax + by = c 的一组解 x0、y0 之后
方程的通解具有以下形式:
x = x0 + k * (b / d)
y = y0 - k * (a / d)
直观理解一下,这是一个设法让正负互相抵消的过程

素数

定义:大于 1 的、只被 1 和它本身整除的正整数称为素数
唯一分解定理
又称算术基本定理:
对于正整数 n,我们一定可以将其写为若干个质数的幂的乘积形式,即
n = ∏ (pi ^ ai),其中 pi 为质数
并且,这种分解是唯一的

埃氏筛法

如果我们要筛出 [1, n] 内的所有素数,使用 [1, √n] 内的素数去筛就可以了
设数组 mark[],mark[i] 表示 i 是否被某个素数筛过
从 2 开始枚举每个数 i:
若 mark[i] = 0,表示 i 没有更小的素因子,从而知道 i 是素数。枚举 i 的所有倍数 j,令 mark[j] = 1
若 mark[i] = 1,知道 i 是一个合数
复杂度 O(n\lg\lg n)
来一段程序吧:

#include<bits/stdc++.h>using namespace std;inline int read(){    char c;    c=getchar();    for(;c>'9'||c<'0';c=getchar());    int sum=0;    for(;c<='9'&&c>='0';c=getchar())sum=sum*10+c-48;    return sum;}//快读int n,m;bool f[10010000];//用来标记当前数是否是素数int main(){    n=read(),m=read();    memset(f,true,sizeof(f));    f[0]=f[1]=false;    for(int i=2;i<=n;i++)    if(f[i])//如果没有被标记是合数    for(int j=2;i*j<=n;f[i*j]=false,j++);//用它去更新所有的倍数。枚举I,所有的j*i就是它的倍数。    for(int i=1;i<=m;i++)    {        int x;        x=read();        if(f[x])printf("Yes\n");        else printf("No\n");    }//这里是判断    return 0;}

埃氏筛法的应用:在进行筛法的同时,我们可以对 [1, n] 内的合数进行素数分解,从而完成一些其他工作,,例如,对于一个素数 i,枚举它的倍数 j 时,把 j 中的所有因子 i 除干净,就知道 j 的质因子分解中 i 的幂了。有一类问题,需要你预处理区间 [L, R] 内的素数分布,其中 R - L <= 10^6, R <= 10^10。此时,区间 [L, R] 的长度比较小;另一方面,√R <= 10^5。我们就可以考虑使用埃氏筛的思想来求解问题。用一个数组 mark2[] 维护区间 [L, R] 被筛的结果,用素数 p 去筛 [L, R] 的计算。为 (R - L) / p,总计算复杂度大概为 H_{R-L},即 O((R - L) \log R)

威尔逊定理

p 是质数的充要条件为
(p - 1)! ≡ -1 (mod p)
充分性
若 p 不是质数,则 gcd( (p-1)!, p ) > 1,与 Bezout 定理相悖
必要性
考虑 [1, p) 中的某个数 x 和它的乘法逆元 y,如果 x != y, 那么 xy ≡ 1,可以令它们互相抵消,于是只需要考虑 x 是自己的逆元的情况,解 x^2 ≡ 1 ==> p | (x - 1) * (x + 1) ,因为 p 是质数,只有可能是 x ≡ ±1,得证

欧拉函数

欧拉函数 φ(n),表示不大于 n 的、与 n 互质的正整数个数,令 n = ∏ (pi^ai),则 φ(n) = n * ∏ (1 - 1/pi)
计算方法
对 n 进行质因子分解的过程中维护,O(√n)
埃氏筛的时候顺便维护,复杂度同埃氏筛
欧拉函数的两个公式
∑ { φ(d) | d | n } = n
证明:在 [1, n] 中,恰好有 φ(d) 个数和 n 的 gcd 为 n / d
n > 1 时, ∑ { d | gcd(d, n) = 1 } = n * φ(n) / 2
证明:当 gcd(x, n) = 1 时,可得 gcd(n - x, n) = 1,可以将与 n 互质的数两两配对
对于 n <= 2 的情况,特殊讨论

剩余系

剩余类
给定 n,整数按照模 n 取值的不同,可以分为 n 个子集,称为剩余类
剩余系
给定 n,从模 n 的 n 个剩余类中各取一个数构成的集合,称为模 n 的一个剩余系,剩余系一般指完全剩余系
简化剩余系
也称既约剩余系,是模 n 的完全剩余系的一个子集,其中每个元素与n 互素,容易验证,简化剩余系中恰好有 φ(n) 个元素

欧拉定理

考虑模 n 的一个简化剩余系 S
因为 S 中的每个元素和 n 互质,它们在模 n 意义下存在乘法逆元
任取一个 a,使得 (a, n) = 1
考虑集合 T = { ax | x ∈ S }
容易验证:|T| = |S|,且 |T| 中的数模 n 互不同余,|T| 中的数均和 n 互质
由此,可得 T 也是模 n 的一个简化剩余系。因为 S 和 T 均为模 n 的简系,可得:
∏ {x | x ∈ S} ≡ ∏ {y | y ∈ T} ==>
∏ {x | x ∈ S} ≡ ∏ {ax | x ∈ S} ==>
∏ {x | x ∈ S} ≡ a ^ {|S|} * ∏ {x | x ∈ S} ==>
a^{|S|} ≡ 1
这就是欧拉定理:
若 (a, n) = 1,则 a ^ φ(n)=1(模意义下)

欧拉定理EXT

由欧拉定理可以推导出一些很有用的结论
费马小定理
若 p 是质数且 (a, p) = 1,则 a ^ (p-1) ≡ 1 (mod p)
一个应用:a ^ (p - 2) ≡ 1 / a (mod p),可以通过快速幂求逆元
欧拉定理 EXT
若 (a, p) = 1,则 a ^ x ≡ a ^ (x % φ(p)) (mod p)
任何情况下,若 x > φ(p),则 a ^ x ≡ a ^ (x % φ(p) + φ(p)) (mod p)

中国剩余定理

可以用来求解这样的同余方程组:给定长度为 k 的数组 a[] 和数组 m[],求解,x ≡ a[i] (mod m[i]),保证 m[i] 两两互质。
推导:定理:令 M = ∏ (m[i]),方程在 [0, M) 中有唯一解 x0,并且通解具有 kM + x0 的形式
这里证明唯一性:
设 x1 != x2 是方程的两个解,我们有
x1 - x2 ≡ 0 (mod m[i])
x1 - x2 是每个 m[i] 的倍数,它们必然也是 M 的倍数,即 M | x1 - x2
于是,x1 和 x2 最多只有一个落在区间 [0, M) 中
解的存在性,我们通过一种构造算法来证明

构造解
令 Mi = M / m[i],因为 (M[i], m[i]) = 1,我们可以找到 Mi 模 m[i] 的乘法逆元 t[i]
考察 Mi * t[i]:
j = i 时,M[i] * t[i] ≡ 1 (mod m[j])
j != i 时,M[i] * t[i] ≡ 0 (mod m[j])
令 x = ∑ (a[i] * M[i] * t[i])
不难验证,x 是满足要求的
模数不互质
模数不互质的时候,怎么办?
将方程两两合并,考虑方程组:
x ≡ a1 (mod m1)
x ≡ a2 (mod m2)
令 x = k1 * m1 + a1 = k2 * m2 + a2
则有 k1 * m1 - k2 * m2 = a2 - a1
由 Bezout 定理判断解的存在性,用 extend_gcd 解方程即可
事实上,可以得到一个推论:
如果存在解,则 x 在 [0, lcm(m1, m2) ) 中有唯一解
应用
可以直接解决一类问题,有些数学计算题,给出的模数 m 并不是一个素数,而很多计算在模素数(或素数的幂)下会方便很多,于是可以将 m 分解质数,求答案模每个素数(的幂)的值,最后使用中国剩余定理合并

LUCAS定理

C(n, m) ≡ C(n / p, m / p) * C(n % p, m % p) (mod p)
证明
核心思想:用两种方法展开 (x + 1) ^ n,考虑 x^m 一项的系数
重要公式:(x + 1) ^ p ≡ x^p + 1 (mod p)

线性筛

一种优秀的筛法
线性复杂度的来源:每个数只会被它的最小的质因子筛掉
这里写图片描述简单证明: 设有一个数 m = p * q * s
其中 p 是最小的质因子,q 是另外一个质因子,如果 m 会被 q 筛掉,那一定是在 i = p * s 时筛掉的,但是我们发现 i = p * s 时,j 枚举到 p 这个质数时就会 break,把每个数被筛的过程画成一张图,发现相同的质因子是在连续的几步被筛掉的。实际上,筛的过程相当于按照质因子降序分解了每个数。

积性函数

若有函数 f(n) 的定义域为正整数,值域为复数,称为数论函数
进一步地,若数论函数 f(n) 满足:对于互质的 p、q,有 f(p * q) = f(p) * f(q),称为积性函数,或者说函数满足积性。更进一步地,若数论函数 f(n) 满足:对于任意 p、q,有 f(p * q) = f(p) * f(q),称为完全积性函数
常见的积性函数
除数函数 σk(n)=∑(d|n) d ^ k,表示n的约数的k次幂和
约数个数函数 τ(n)=σ0(n)=∑(d|n) 1,表示n的约数个数,一般也写为d(n)。
约数和函数 σ(n)=σ1(n)=∑(d|n) d,表示n的约数之和
欧拉函数 φ(n)
莫比乌斯函数μ(n):
n 有平方因子时值为 0
否则值为 (-1) ^ (质因子个数)
元函数 e(n) = [n = 1],完全积性
恒等函数 I(n) = 1,完全积性
单位函数 id(n) = n,完全积性

Dirichlet卷积

对两个数论函数进行的运算
设我们有两个数论函数 f(n) 和 g(n)
它们的狄利克雷卷积是一个新的函数 (f * g) (n)
设这个函数为 h
我们有 h(n) = ∑(k|n) f(k) * g(n / k)
Dirichlet卷积的性质
积性函数的狄利克雷卷积仍然满足积性
证明:对互质的 p、q,有
h(p) * h(q)
= ∑ { f(a) * g(b) | ab = p }* ∑ { f(c) * g(d) | cd = q }
= ∑(ab = p, cd = q) f(ac) * g(bd)
=∑(xy = pq) f(x) * g(y)
=h(p * q)
注意:完全积性函数的狄利克雷卷积不一定满足完全积性。
狄利克雷卷积满足结合律,即对于三个数论函数 f、g、h,有 (f * g) * h = f * (g * h)
证明:
(f * g) * h (n)
= ∑(ab = n) h(b) * (∑(xy = a) f(x) * g(y))
= ∑(xyb = n) f(x) * g(y) * h(b)
= ∑(xy = n) f(x) * (∑(ab = y) g(a) * h(b))
= f * (g * h) (n)
Dirichlet 卷积同时也具有交换律、分配律
Dirichlet 卷积运算存在单位元(元函数 e):f * e = e * f = f。
常见的公式
id = �� * 1
d = 1 * 1
σ = id * 1
e = 1 * μ (反演式) *
�� = id * μ *
积性函数的预处理
借助线性筛,可以在 O(n) 时间内预处理出某种数论函数在 [1, n] 的取值
我们以约数个数函数为例说明
莫比乌斯函数、欧拉函数、约数和函数的预处理都应该熟练掌握
约数个数函数
n = ∏ (pi^ai)
d(n) = ∏ (ai + 1)
我们维护一个辅助数组 num[x] ,表示 x 的最小的质因子的幂次
线性筛每次会筛掉一个数最小的质因子,这让我们可以很方便地维护 num[],从而计算 d[]
这里写图片描述

大步小步算法(bSGS)

我们设置一个步长 m
则任意一个可行解可以写成 x = am + b (0 <= b < m)
A ^ (am + b) = B mod P => A ^ b = B * A ^ (-am) mod P
把 0 <= b < m 的 A ^ b 预先算出来,扔到哈希表中
查询的时候暴力枚举 a,算出等式右边,在哈希表中查询
复杂度 O(P / m) + O(m)
m = √P 时,取得最优复杂度 O(√P)
如果用 map 实现哈希表,还要乘一个 log

扩展大步小步法

如果 P 不是质数,怎么办?
若 (A, P) = 1,算法还是可以跑的,求逆元改成 extend_gcd 就行了
(A, P) != 1时,问题出在哪儿?
A 不存在关于 P 的逆元
令 d = gcd(A, P)
A/d * A ^ (x - 1) = B/d (mod (P/d) )
递归求解
最多 log(P) 层,注意最后算出来的答案 x 要加上层数
因此,需要特判掉 x <= log(P) 的情况

总结

黑色星期一??蒟蒻被虐的第一天。数论越讲到后面越听不懂,上面列出的所有知识点都要自己好好去学下。

原创粉丝点击