蓝桥杯辅导课笔记

第一讲 递归与递推

递归

将问题分解为若干个同种子问题

自己调用自己

所有递归都可以转化为递归搜索树来理解

斐波那契数列

AcWing 717. 简单斐波那契 - AcWing

int f(int n)
{
    if(n == 1) return 0;
    if(n == 2) return 1;
    return f(n - 1) + f(n - 2);
}

指数型枚举

AcWing 92. 递归实现指数型枚举 - AcWing

const int N = 16;
int n;
bool sta[N];
void dfs(int u)
{
    if (u > n)
    {
        for (int i = 1; i <= n; i++)
        {
            if (sta[i])
                cout << i  << ' ';
        }
        cout << endl;
        return;
    }
​
    //选
    sta[u] = true;
    dfs(u + 1);
    sta[u] = false;
​
    //不选
    dfs(u + 1);
}

组合型枚举

AcWing 93. 递归实现组合型枚举 - AcWing

const int N = 30;
​
int sta[N], m, n;
bool used[N];
​
void dfs(int u, int start)
{ 
    if (u > n)
    {
        for (int i = 1; i <= n; i++)
            printf("%d ", sta[i]);
        puts("");
        return ;
    }
    for (int i = start; i <= m; i++)
    {
        sta[u] = i;
        dfs(u + 1, i + 1);
        sta[u] = 0;
    }
}

优化:剪枝

if(u + m - start < n)
    return ;

排列型枚举

AcWing 94. 递归实现排列型枚举 - AcWing

const int N = 10;
int n;
int sta[N];
bool use[N];
​
void dfs(int u)
{
    if (u > n)
    {
        for (int i = 1; i <= n; i++)
            cout << sta[i] << ' ';
        cout << endl;
        return;
    }
​
    for (int i = 1; i <= n; i++)
    
        if (!use[i])
        {
            sta[u] = i;
            use[i] = true;
            dfs(u + 1);
            sta[u] = 0;
            use[i] = false;
        }
}

递推

先求子问题,用子问题推原问题

斐波那契数列

int main()
{
    int n;
    cin >> n;
    int f[46];
    f[1] = 0, f[2] = 1;
    
    for(int i = 3; i <= n; i++)
        f[i] = f[i - 1] + f[i - 2];
    for(int i = 1; i <= n; i++)
        cout << f[i] << ' ';
    return 0;
}

优化:不开数组,使用两个变量(滚动数组雏形)

int main()
{
    int n;
    cin >> n;
    
    int a = 0, b = 1;
    for(int i = 1; i <= n; i++)
    {
        cout << a << '';
        int fn = a + b;
        a = b, b 
    }
    
    return 0;
}

习题

AcWing 95. 费解的开关 - AcWing

AcWing 1209. 带分数 - AcWing

AcWing 116. 飞行员兄弟 - AcWing

AcWing 1208. 翻硬币 - AcWing

第二讲 二分和前缀和

二分法

整数二分

步骤:

  1. 找一个区间[L, R],使得答案一定在区间中

  2. 找一个判断条件, 使得该判断体哦阿健具有二段性,并且答案一定是改二段性的分界点。

  3. 分析重点M在改判断条件下是否成立,考虑在哪个区间。

  4. 如果更新方式是 R = M,则不用做任何处理;如果是 L= M, 则需要在计算M时加1。

模板:

  1. 将[l, r]划分为[l, M]和[M + 1, r]

    bool check(int x)
    {
        /*
        是否满足某种性质
        */ 
    }
    int bsearch_1(int l, int r)
    {
        while(l < r)
        {
            int M = l + r >> 1; //右移
            if(check(M)) r = M;
            else l = M + 1;
        }
        return l;
    }

  2. 将[l, r]划分为[l, M - 1]和[M, r]

    bool check(int x)
    {
        /*
        是否满足某种性质
        */
    }
    int bsearch_2(int l, int r)
    {
        while(l < r)
        {
            int M = l + r + 1 >> 1; //右移
            if(check(M)) l = M;
            else r = M - 1;
        }
        return l;
    }

实数二分

模板:

bool check(int x)
{
    /*
    是否满足某种性质
    */
}
int bsearch_3(double l, double r)
{
    const double eps = 1e-6;
    while(r - l > eps)
    {
        double mid = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

前缀和

对数据预处理

一维前缀和

S[i] = a[1] + a[2] + a[3] + ... + a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
int n, m;
int a[N];   // 表示原数组
int s[N];   // 表示前缀和数组
​
int main()
{
    scanf("%d%d", &n, &m);
​
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        s[i] = s[i - 1] + a[i];
    }
//合并写
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &s[i]);
        s[i] += s[i - 1];
    }
    while (m -- )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]);
    }
​
    return 0;
}

二维前缀和

  1. 计算前缀和

S[x][y] = S[x - 1][y] + S[x][y - 1] - S[x - 1][y - 1] + a[x][y]
  1. 计算子矩阵和

S[x, y]为第x行y列左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

习题

AcWing 789. 数的范围 - AcWing

AcWing 790. 数的三次方根 - AcWing

AcWing 795. 前缀和 - AcWing

AcWing 796. 子矩阵的和 - AcWing

AcWing 730. 机器人跳跃问题 - AcWing

AcWing 1221. 四平方和 - AcWing

AcWing 1227. 分巧克力 - AcWing

AcWing 99. 激光炸弹 - AcWing

AcWing 1230. K倍区间 - AcWing

第三讲 数学与简单DP

数学

  • 如果 a,b 均是正整数且互质,那么由ax + by, x ≥ 0, y ≥ 0不能凑出的最大数是 ab − a − b。

简单DP

DP问题 一般为最值和个数。

从集合角度分析DP问题

  1. 化零为整:状态表示

    1. 集合

    2. 属性:Max/Min/Count

  2. 化整为零:状态计算

    集合的划分(划分依据:找最后一个不同点)

背包问题

  • 0 - 1背包:每件物品最多使用一次

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。

  • 完全背包:每件物品使用不限次数

  • 多重背包:每件物品的使用次数有限制

    • 优化

  • 分组背包:有N组, 每组有若种,每种有若干个

习题

AcWing 1205. 买不到的数目 - AcWing

AcWing 1211. 蚂蚁感冒 - AcWing

AcWing 1216. 饮料换购 - AcWing

AcWing 2. 01背包问题 - AcWing

AcWing 1015. 摘花生 - AcWing

AcWing 895. 最长上升子序列 - AcWing

AcWing 1212. 地宫取宝 - AcWing

AcWing 1214. 波动数列 - AcWing

第四讲 枚举、模拟与排序

枚举

例题

AcWing 1210. 连号区间数 - AcWing

AcWing 1236. 递增三元组 - AcWing

AcWing 1245. 特别数的和 - AcWing

AcWing 1204. 错误票据 - AcWing

AcWing 466. 回文日期 - AcWing

枚举日期做法

AcWing 466. 回文日期 - AcWing

归并排序

AcWing 787. 归并排序 - AcWing

分治思想

  1. 找分界点mid = l + r >> 1

  2. 递归排序left, right

  3. 归并——合二为一:利用双指针

void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;
​
    int mid = l + r >> 1;
​
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);
​
    int i = l, j = mid + 1, k = 0;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tem[k++] = q[i++];
        else tem[k++] = q[j++];
    while (i <= mid) tem[k++] = q[i++];
    while (j <= r) tem[k++] = q[j++];
​
    for (int i = l, j = 0; i <= r; j++) q[i] = tem[j];
}

习题

AcWing 1219. 移动距离 - AcWing

AcWing 1229. 日期问题 - AcWing

AcWing 1231. 航班时间 - AcWing

AcWing 1241. 外卖店优先级 - AcWing

AcWing 788. 逆序对的数量 - AcWing

第五讲 树状数组与线段树

应用范围 :线段树包含树状数组

树状数组

代码短, 常数小

O(longn):给某个位置上的数加上一个数(单点修改), 求某一个前缀和(区间查询)

/*
数组下标为奇数,树状数组与原数组相同.
判断c[x]为第几层 :x二进制末尾有多少个0,假设x二进制末尾有k个0,则c[x]表示(x - 2 ^ k, x]的和(2 ^ k = lowbit(x))。
*/
​
int arr[N], tr[N];
​
int lowbit(int x)
{
    return x & -x;
}
​
void add(int x, int v)
{
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;
}
//求和
int query(int x)
{
    int res = 0;
    for (int i = x; i ; i -= lowbit(i)) res += tr[i];
    return res;
}
​
int main()
{
    //初始化树状数组
    for (int i = 1; i <= n; i++) add(i, arr[i]);
}

AcWing 1264. 动态求连续区间和 - AcWing

AcWing 1265. 数星星 - AcWing

线段树

是二叉树

操作

  1. 单点修改(O(logn)):递归+回溯

  2. 区间查询(O(logn)):递归

  3. 区间修改:需用懒标记,复杂

函数

  1. pushup:用子节点信息更新当前节点信息(有时省略)

  2. build:在一段区间上初始化线段树

  3. modify:修改

  4. query:查询

存储

与堆的存储方式类似, 存在一维数组内

下标为x:父节点为x / 2下取整(x >> 1), 左儿子为2 * x(x << 1), 右儿子为2 * x + 1(x << 1 | 1)

const int N = 100010;
int n, m;
int a[N];
​
struct Node
{
    int l, r;
    int sum;
}tr[4 * N];
​
void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
​
void build(int u, int l, int r)
{
    if (l == r) tr[u] = { l, r, a[r] };
    else
    {
        tr[u] = { l, r };
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}
​
int query(int u, int l, int r)  //根节点为u 查询[l, r]的和
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
​
    int mid = tr[u].l + tr[u].r >> 1;
    int sum = 0;
    if (l <= mid) sum += query(u << 1, l, r);
    if (r > mid) sum += query(u << 1 | 1, l, r);
    return sum;
}
​
void modify(int u, int x, int v) //根节点为u 在x位置上加v
{
    if (tr[u].l == tr[u].r) tr[u].sum += v;
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);  
        pushup(u);
    }
}

AcWing 1264. 动态求连续区间和 - AcWing

AcWing 1270. 数列区间最大值 - AcWing

习题

AcWing 1215. 小朋友排队 - AcWing 归并排序求逆序对

AcWing 1228. 油漆面积 - AcWing 扫描线法 + “非常特殊的线段树”(懒标记不传递)

第六讲 双指针、BFS与图论

双指针

AcWing 1238. 日志统计 - AcWing

BFS

每次取出队头元素,将扩展出的所有元素放在队尾。

组成:判重数组(一般入队时判重),队列

框架

queue <- 初始状态;
while(queue非空)
{
    t <- 队头;
    队头出队;
    for(扩展t)
    {
        ver <- 新节点;
        if(!st[ver]) ver -> 队尾;
    }
}

AcWing 1101. 献给阿尔吉侬的花束 - AcWing

Flood Fill的实现

BFS DFS

AcWing 1113. 红与黑 - AcWing

图论

AcWing 1224. 交换瓶子 - AcWing

  • 交换同一环内的点:裂成两个环

  • 交换不同环中的点:并成一个环

习题

AcWing 1240. 完全二叉树的权值 - AcWing

AcWing 1096. 地牢大师 - AcWing

AcWing 1233. 全球变暖 - AcWing

AcWing 1207. 大臣的旅费 - AcWing

AcWing 826. 单链表 - AcWing

第七讲 贪心

AcWing 1055. 股票买卖 II - AcWing

AcWing 104. 货仓选址 - AcWing

AcWing 122. 糖果传递 - AcWing

AcWing 112. 雷达设备 - AcWing

该题策略:

  1. 将所有区间右端点排序

  2. 扫描每个线段

    1. 上一个点不在区间中,则选右端点

    2. 上一个点在区间中, 则跳过该区间

习题

AcWing 1235. 付账问题 - AcWing

AcWing 1239. 乘积最大 - AcWing

AcWing 1247. 后缀表达式 - AcWing

AcWing 1248. 灵能传输 - AcWing

第八讲 数论

最大公约数

欧几里德算法——辗转相除法

(a, b) = (a, a mod b)

//直接用
__gcd();
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

AcWing 1246. 等差数列 - AcWing

扩展——裴蜀定理

/*
指对于任意正整数对(a,b),一定存在非零整数x和y,使得 ax+by=gcd(a,b);
分情况讨论一下扩展欧几里得定理:
(1)当b = 0时,a和b的最大公约数为a.则x = 1;
(2)当b ≠ 0时,by+(a mod b)x = gcd(a,b)
->by+(a - a/b * b)x = gcd(a,b)
->ax+b(y - a/b * x) = gcd(a,b)
即当我们用扩展欧几里得定理求x和y时,欧几里得定理每递归一次x不用变,y->y-a/b * x即可
​
如果我们求出x和y的一对,我们记为x0和y0
那么其他的x和y可以通过x0,y0表示:
令a’=a/gcd(a,b) , b’=b/gcd(a,b);
那么其他的x和y可以表示为:x=x0+kb’,y=y0-ka’;
证明:
ax0+by0 = gcd(a,b)
ax+by = gcd(a,b)
如果x0比x小,那么y0就应该比y大,这样加起来的结果才可能相同
所以,a(x-x0) = b(y0-y);
两边同时除以一个gcd(a,b)
得到:a’(x-x0) = b’(y0-y)
那么b’就应该能整除a’(x-x0),又因为a’和b’互质,所以b’应该能整除(x-x0)
所以(x-x0) = kb’
则 x = x0+kb’
y = y0-ka’
这里的k可正可负
*/
​
int exgcd(int a, int b, int &x, int &y)
{
    if(!b)
    {
        x =  1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

AcWing 1299. 五指山 - AcWing

算术基本定理

N = P1 ^ α1 + P2 ^ α2 + P3 ^ α3 + ... + Pk ^ αk 

AcWing 1295. X的因子链 - AcWing

筛法求素数——线性筛法(欧拉筛法)

可在O(n)下求出1~n中所有质数, 以及每个数的最小质因子

int primes[N], cnt; //存所有质数
bool st[N]; //当前质数是否被筛
int minp[N];
​
int get_primes(int n)
{
    for(int i = 2; i <= n; i++)
    {
        if(!st[i]) minp[i] = i, primes[cnt++] = i;
        for(int j = 0; primes[j] * i <= n; j++)
        {
            st[primes[j] * i] = true;
            minp[primes[j] * i] = primes[j];
            if(i % primes[j] == 0) break;
        }
    }
}

约数定理

约数个数:(α1 + 1)(α2 + 1)(α3 + 1) ... (αk + 1)

约数之和:(1 + P1 + P1 ^ 2 + P1 ^ 3 + ... + P1 ^ α1)(1 + P2 + P2 ^ 2 + P2 ^ 3 + ... + P2 ^ α2) ... (1 + Pk + Pk ^ 2 + Pk ^ 3 + ... + Pk ^ αk)

习题

AcWing 1223. 最大比例 - AcWing

AcWing 1301. C 循环 - AcWing

AcWing 1225. 正则问题 - AcWing

AcWing 1243. 糖果 - AcWing

第九讲 复杂DP

整数划分

AcWing 1050. 鸣人的影分身 - AcWing

背包问题

AcWing 1047. 糖果 - AcWing

区间DP

AcWing 1222. 密码脱落 - AcWing

较优循环方式

循环区间长度, 枚举左端点, 求出右端点

树形DP

AcWing 1220. 生命之树 - AcWing

递归

第十讲 疑难杂题

例题

AcWing 1242. 修改数组 - AcWing 并查集

并查集

  1. 普通并查集:p[i]是i的父节点,i所在树的根节点是代表元素

  2. 单链表式并查集:p[i]是单链表中的下一个节点,i所在树的根节点是从i开始向右找,第一个没有被用过的位置

零碎知识点

  • 一般ACM或者笔试题的时间限制是1秒或2秒。 在这种情况下,C++代码中的操作次数控制在 10^7 ∼ 10^8为最佳。下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:

n≤30, 指数级别, dfs+剪枝,状态压缩dp
n≤10^2 => O(n^3),floyd,dp,高斯消元
n≤10^3=> O(n^2),O(n^2logn),dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford
n≤10^4 => O(n∗√n),块状链表、分块、莫队
n≤10^5 => O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、Kruskal、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树
n≤10^6 => O(n), 以及常数较小的 O(nlogn)算法 => 单调队列、 hash、双指针扫描、并查集,kmp、AC自动机,常数比较小的 O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
n≤10^7 => O(n),双指针扫描、kmp、AC自动机、线性筛素数
n≤10^9 => O(√n),判断质数
n≤10^18 => O(logn),最大公约数,快速幂,数位DP
n≤10^1000 => O((logn)^2),高精度加减乘除
n≤10^100000 => O(logk×loglogk),k表示位数,高精度加减、FFT/NTT
  • 输入规模小于10^5 时,cin和cout与scanf和printf效率相似;而大于10^5 时,prinf和scanf效率高出一倍。

  • 全局变量初值为0,局部变量初值为随机数。

  • 上取整

(int)ceil(a / b);
(a + b - 1) / b;
  • 使模运算结果始终为正数

( a % b + b) % b;
  • 排序

sort();`    
  • 字符型变整型

int x = 0;
for(int i = 0; i < str.size(); i++)
    x = x * 10 + str[i] - '0';

或使用 stoi函数

  • 读取整行数据

#include
#include
#include
#include
​
using namespace std;
​
int a[N],
    
int main()
{
    int cnt; //行数
    cin >> cnt;
    string line;
    getline(cin, line); //忽略掉第一行回车
    
    while(cnt --)
    {
        getline(cin, line);
        stringstream ssin(line);
        
        while(ssin >> a[n]) n++;
    }
​
    return 0;
}
  • 二元组排序

    • 结构体

    • pair(默认以first为第一关键字排序)

  • 队列:先进先出 bfs

栈:先进后出 递归 dfs

  • 结构体内嵌套比较函数

struct Segment
{
    double l, r;
    bool operator< (const Segment& t)const
    {
        return r < t.r;
    }
}s;
~i 等价于 i != -1;

快速幂

int quick_power(int a, int k, int p)  // 求a^k mod p
{
    L res = 1 % p;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}   
  • 避免处理浮点数:把除化为乘 AcWing 3490. 小平方 - AcWing

你可能感兴趣的:(c++)