CSP初赛复习

一.存储知识:

1.存储:

编码:
比特(bit):编码最小的单位。
字节(byte):存储的最小单位。

2.存储单位的转化:

1 KB = 1024 Byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB

3.常见的变量类型的存储大小:

1个 int = 4个 Bytes
1个 (unsigned) long long = 8个 Bytes(unsigned long long 不存负数,存非负数是long long 的两倍)
1个 bool = 1个 Byte
1个 char = 1个 Byte
1个 float = 4个 Bytes
1个 double = 8个 Bytes

二.计算机系统的组成:

硬件系统(hardware)+软件系统(software)。

1.硬件系统:

(1):硬件系统包括主机CPU内存...)以及外接设备输入输出设备等)。

(2):内存分为 RAM(可读写存储器,掉电之后数据清除) 、ROM(只可读存储器,掉电之后数据保留) 和 Cache(高速缓冲存储器,为了适应CPU的高速计算) 三种。

(3):register(寄存器)是CPU里的一个小区域,用来存变量。一般变量存在内存,而 register 可以将变量存到CPU里去,以达到加快运行速度的目的,但一般用于存大量重复的变量,例如循环变量:for(register int i=1;i<=n;i++)之类的。

2.软件系统:

软件系统包括操作系统(Linux、Windows、Mac等)以及应用软件(APP)。

三.进制转化:

(1)十进制转 \(m\) 进制——短除法:
如图示:

(2) \(m\) 进制转十进制——逐位乘后求和:
如:
\((10110)_2 = 1 \times 2^4 + 0 \times 2^3 + 1 \times 2^2 + 1 \times 2^1 + 0 \times 2 ^0 = (14)_{10}\)

四.位运算和位移操作:

1.位运算(支持各种运算率):

优先级:括号 \(>\) 四则运算 \(>\) 逻辑运算 \(>\) 位运算

(1):按位取反,符号~,将一个整数在二进制下逐位取反。
(2):按位与,符号&,将两个整数在二进制下对齐个位逐位比较,数码同为 \(1\) 则为 \(1\),否则为 \(0\)
(3):按位或,符号|,将两个整数在二进制下对齐个位逐位比较,数码同位有 \(1\) 则为 \(1\),否则为 \(0\)
(4):按位异或,符号^/xor,将两个整数在二进制下对齐个位逐位比较,数码不同则为 \(1\),否则为 \(0\)

2.位移操作:
(1):左移: 符号<<,左移一位相当于 $ \times 2 $ 。
(2):右移: 符号>>,右移一位相当于 $ \times \dfrac{1}{2}$。

五.编码:

(1):编码包括原码反码补码

(2)原码:正负用符号位区分,正数第一位为 \(0\),后接数值,负数则为 \(1\)
eg: \(5\) 原码为 \(0\ 101\)\(-5\) 原码为 \(1\ 101\)

(3)反码:正数的反码就是原码,负数的反码是符号位不变,数值部分取反。
eg: \(-5\) 反码为 \(1\ 010\)

(4)补码:正数的补码还是本身,负数的补码是反码 \(+1\)

例题:在8位二进制补码中,\(10101011\) 表示十进制下的_____?

\(ans:\) -85。

六.数据结构:

(1):初赛常考:线性数据结构树形数据结构图类数据结构

(2)线性数据结构: 数组(包括静态数组和动态数组)、队列(先进先出)、(后进先出)、链表(一环扣一环、无法直接访问任意元素)。

栈常考进出栈顺序及前后缀表达式。
前缀表达式:指将四则运算的符号放在最前面,两个数值放在最后面的表达式。
后缀表达式:指将四则运算的符号放在最后面,两个数值放在最前面的表达式。

例题:
\(a - b \times c + d \times (f - e)\) 的前缀表达式: \(+\ -\ a \times b\ c \times d -f\ e\)
\(a - b \times c + d \times (f - e)\) 的后缀表达式:\(a\ b\ c \times -\ d\ f\ e\ - \times \ +\)

(3)树的概念:
树是一种特殊的图,所有结点连通,且 \(n\) 个点 \(n-1\) 条边,树中一定没有环。
在树的结点中,包含父节点、子节点(相对关系)。
根节点是没有父节点的节点,根据是否约定根节点,分为有根树(约定了根节点或边有向的树)或无根树(没有约定根节点且边无向的树)。
叶节点是没有子节点的节点。

(4)特殊的树形结构:
二叉树:对于每个节点,最多只有两个子节点的树。
完全二叉树:除了最后一层,是一颗完美二叉树,且最后一层的节点尽量靠左分布的二叉树。
二叉搜索树:对于任意一个节点 \(x\),其左子树中的权值均不超过 \(x\) 的权值,其右子树中的节点权值均超过 \(x\) 的权值的二叉树。

(5)二叉树中的一些性质:
树的深度(高度):描述一棵树的节点层数,需要约定根节点是 \(0\) 还是 \(1\)

二叉树中的数量性质:
1.若深度从 \(1\) 计算,则二叉树第 \(i\) 层上最多有节点 \(2^{i-1}\) 个。
2.:若深度从 \(1\) 计算,则深度为 \(h\) 的二叉树最多有 \(2^h-1\) 个节点
3.:若深度从 \(1\) 计算,那么 \(n\) 个节点的二叉树深度至多为 \(n\),至少为 \(\lceil \log_{n+1}\rceil\)
4.:所有的二叉树都可以直接用数组来存储,若节点编号为 \(i\),则下标也为 \(i\),且左孩子为 \(i \times 2\),右孩子为 \(i \times 2 + 1\)

(6)树的遍历:
1.前序遍历(根左右):对于每个节点 \(x\),满足先输出 \(x\),再输出 \(x\) 的左孩子,最后输出 \(x\) 的右孩子。
2.中序遍历(左根右):对于每个节点 \(x\),满足先输出 \(x\) 的左孩子,再输出 \(x\),最后输出 \(x\) 的右孩子。
3.后序遍历(左右根):对于每个节点 \(x\),满足先输出 \(x\) 的左孩子,再输出 \(x\) 的右孩子,最后输出 \(x\)
4.层序遍历:从上到下,从左到右。
5.由二叉树的形态可以唯一得到遍历顺序,反之不行,但 先序+中序后序+中序 可以唯一确定二叉树的形态。

代码实现:

#include<bits/stdc++.h>
using namespace std;

struct Node
{
    int lc,rc;
}a[MAXN];

inline void dfs1(int x)//前序遍历
{
    if(x==0)
        return;
    printf("%d\n",x);
    dfs1(a[x].lc);
    dfs1(a[x].rc);
    return;
}

inline void dfs2(int x)//中序遍历
{
    if(x==0)
        return;
    dfs2(a[x].lc);
    printf("%d\n",x);
    dfs2(a[x].rc);
    return;
}

inline void dfs3(int x)//后序遍历
{
    if(x==0)
        return;
    dfs3(a[x].lc);
    dfs3(a[x].rc);
    printf("%d\n",x);
    return;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n;
    cin>>n;
    for(register int i=1;i<=n;i++)
        cin>>a[i].lc>>a[i].rc;
    dfs1(1);//均以1为根
    dfs2(1);
    dfs3(1);
    return 0;
}

例题 P1030 求先序排列

Code

#include<bits/stdc++.h>
using namespace std;

inline void dfs(string z,string h)
{
    if(z.size()==0)
        return;
    char rt=h[h.size()-1];
    int p=z.find(rt);
    cout<<rt;
    dfs(z.substr(0,p),h.substr(0,p));
    dfs(z.substr(p+1),h.substr(p,h.size()-p-1));
    return;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    string z,h;
    cin>>z>>h;
    dfs(z,h);
    return 0;
}

例题 P1827 美国血统 American Heritage

Code

#include <bits/stdc++.h>
using namespace std;
string a,b;

inline void dfs(int x,int y,int p,int q)
{
    if(x>y || p>q)
        return;
    else
    {
        int i=b.find(a[x]);
        dfs(x+1,x+i-p,p,i-1);
        dfs(x+i-p+1,y,i+1,q);
        cout<<a[x];
    }
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>b>>a;
    int len=a.length()-1;
    dfs(0,len,0,len);
    return 0;
}

(7)图的概念:
由点和线构成的网络。

(8)图的分类:
有四种方式:
1.可分为有向图(边有向或有箭头)和无向图(边无向或无箭头)。
2.可分为有权图(边有权重)和无权图(边没有权重)。
3.可分为有环图(存在至少一个环)和无环图(不存在环)。
4.可分为稀疏图(点多边少)和稠密图(边多点少、边很密集)。

(9)图顶点的度数:
1.有向图:分为入度(一个点被多少箭头指向)和出度(一个点指出的箭头数量)。

2.无向图:一个点连接的边的数量(无向图不分入度出度),

(10)图的连通性:
若图中有点 \(x\) 能够找到一条路径到达 \(y\),则称 \(x\)\(y\) 连通。

完全图:图中的每一个点都与其余所有点有直接连边。

对于有 \(n\) 个点的无向完全图,其边数为 \(\dfrac{n \times (n-1)}{2}\)

对于有 \(n\) 个点的有向完全图,其边数为 \(n \times (n-1)\)

(11)图的存储:
邻接矩阵,设二维数组 \(g_{i,j}\) 表示点 \(i\) 指向点 \(j\) 的一条边。
邻接链表,设动态数组vector<int>nbr[105];
比如 \(2\)\(5\) 有一条边,那么nbr[2].push_back(5)

也可用链式前向星:

struct edge
{
    int to,nxt,len;
}e[MAXN<<1];

int head[MAXN],cnt;

inline void add(int x,int y,int z)//从点x到点y连一条权值为z的边
{
    e[++cnt].to=y;
    e[cnt].len=z;
    e[cnt].nxt=head[x];
    head[x]=cnt;
    return;
}

七.排序算法:

(1)冒泡排序:时间复杂度 \(O(n^2)\),稳定。
(2)选择排序:时间复杂度 \(O(n^2)\),不稳定。
(3)插入排序:时间复杂度 \(O(n^2)\)(可以做到 \(O(n)\)),稳定。

八.数学相关的问题:

(1)加法原理:做一件事情,有 \(n\) 种做法,第 \(i\) 种做法有 \(a_i\) 种方式,则完成这件事情有 \(\sum\limits_{i=1}^n a_i\) 种不同的方法。

(2)乘法原理:做一件事情,有 \(n\) 个步骤,第 \(i\) 个步骤有 \(a_i\) 种方式,则完成这件事情有 \(\prod\limits_{i=1}^n a_i\) 种不同的方法。

(3)排列数公式:\(n\) 个数种选 \(m\) 个数构成 \(1\) 组有序的排列,方案数为:

\[A_n^m=\dfrac{n!}{(n-m)!} \]

(4)组合数公式:\(n\) 个数种选 \(m\) 个数构成 \(1\) 组无序的组合,方案数为:

\[C_n^m=\dfrac{A_n^m}{A_m^m}=\dfrac{n!}{(n-m)!\ m!} \]

例1: \(8\) 个人排成一队。
1.要求甲乙两人必须相邻。
解:

\[A_7^7 \times 2 \]

2.甲乙不相邻。
解:

\[A_8^8-(A_7^7 \times 2) \]

3.甲乙相邻但是与丙不相邻。
解:

\[A_7^7 \times 2-A_6^6 \times 4 \]

4.甲乙相邻且丙丁相邻。
解:

\[A_6^6 \times 4 \]

例2:有人射击,开了 \(8\) 枪,命中 \(4\) 枪,恰好有 \(3\) 枪连续命中的方案有多少?

解:在五个空中选取 \(2\) 个排列,即 \(A_5^2\)

(5)卡特兰数(Catalan):
\(C_n\) 表示在 \(n+2\) 条边的凸多边形中,可以画 \(n-1\) 条不相交的对角线,将该多边形分成 \(n\) 个三角形。

\(C_n\) 也可表示所有在 \(n \times n\) 格点中不越过对角线的单调路径的个数。

递推式:

\[C_n=\sum\limits_{k=0}^{n-1} C_k \times C_{n-k-1} \]

通项公式一:

\[C_n=\dfrac{1}{n+1} \times \dbinom{n}{2n}=\dfrac{(2n)!}{(n+1)!\ n!} \]

通项公式二:

\[C_n=\dbinom{2n}{n}-\dbinom{2n}{n+1}\ (n \geqslant 1) \]

通项公式二其实就是把通项公式一拆开。

那么我们这里来证一下第二个:

根据定义,合法的路径不能越过对角线。那么我们考虑利用总方案数 \(C_{2n}^n\) 减去不合法的方案数。

这里的黄色线和绿色线连接而成的路径代表一条不合法的路径,我们把路径第一次超越对角线的点(点 \(L\))到终点(点 \(A\))的路径沿粉色线条(对角线向上平移 \(1\) 个单位得到)对称。得到了蓝色的路径。显然,这里的黄色路径和蓝色路径是对应的。

而之所以选择“向上平移 \(1\) 个单位”的意义就是在任何情况下接触这条线就会变成不合法路径。

容易证明,所有不合法的路径和所有从原点到A'点的路径都是一一对应的。所以不合法的路径条数就是从原点到A'的路径条数 \(C_{2n}^{n-1}\)

证明如下:

因为这个关系是可逆的(通过操作后的路径能得出操作前的路径),所以显然所有不合法的路径和所有从原点到 \(A'\) 点的路径都是一一对应的。

这样就得出了前面提到的卡特兰数第二个通项公式:

\[C_n=\dbinom{2n}{n}-\dbinom{2n}{n+1}\ (n \geqslant 1) \]

那么现在我们来应用一下:

例1:有一个算式,包含 \(n\) 运算符及 \(n+1\) 个运算数值,要求在算式中加上 \(1\) 对括号,求不同的运算顺序有多少种?

解:这种题目基本用卡特兰数解决,结果就是一个二叉树。

例2:\(n\) 个节点的二叉树,不同的形态有多少种?

解:很明显我们分别考虑左右子树,那显然就是卡特兰数的递推式。

放球问题:\(n\) 个球放入 \(m\) 个盒子中:
1.球同,盒子相同,不允许空盒子;(只能穷举法)
2.球同,盒子相同,允许空盒子;(只能穷举法)

3.球同,盒子不同,不允许空盒子;(隔板法)\(C_{n-1}^{m-1}\)
4.球同,盒子不同,允许空盒子;(隔板法)\(C_{n+m-1}^{m-1}\)

5.球不同,盒子相同,不允许空盒子;(第二类斯特林数)
6.球不同,盒子相同,允许空盒子。(第二类斯特林数)

第二类斯特林数:\(\begin{Bmatrix}n\\m\end{Bmatrix}\) 表示 \(n\) 个不同元素分为 \(m\) 个集合的方案数。

递推式:

\[\begin{Bmatrix}n\\m\end{Bmatrix}=\begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m \times \begin{Bmatrix}n-1\\m\end{Bmatrix} \]

说白了就是第 \(n\) 行第 \(m\) 个数等于它上一行第 \(m-1\) 个数加上一行第 \(m\) 个数乘 \(m\)

通项公式:

\[\begin{Bmatrix}n\\m\end{Bmatrix}=\dfrac{1}{m!}\sum\limits_{i=0}^{\infty}(-1)^{m-i}\dbinom{m}{i}i^n \]

第一类斯特林数:\(\begin{bmatrix} n\\m\end{bmatrix}\) 表示 \(n\) 个不同元素分为 \(m\) 个圆排列的方案数

有标号的第一类斯特林数:\(s(n,m)=(-1)^{n-m}\begin{bmatrix} n\\m\end{bmatrix}\)

递推式:

\[\begin{bmatrix} n\\m\end{bmatrix}=\begin{bmatrix} n-1\\m-1\end{bmatrix}+(n-1)\begin{bmatrix} n-1\\m\end{bmatrix} \]

即新建一个圆排列,或者插入前面 \(n-1\) 个元素中任意一个的后面。

那么显然,有标号的就是:

\[\begin{bmatrix} n\\m\end{bmatrix}=\begin{bmatrix} n-1\\m-1\end{bmatrix}-(n-1)\begin{bmatrix} n-1\\m\end{bmatrix} \]

生成函数有些复杂,这里就不说了。

posted @ 2022-08-06 19:22  Code_AC  阅读(751)  评论(0编辑  收藏  举报