深入浅出程序设计竞赛(基础篇)
一、语言入门
1. 简简单单写程序
2. 顺序结构程序设计
P5705 【深基2.例7】数字反转
scanf("%c%c%c.%c",&a,&b,&c,&d);
P1425 小鱼的游泳时间
把时间转换成距 \(00:00\) 经过了多少分钟
P5708 【深基2.习2】三角形面积
三角形三边长 \(a,b,c\),面积 \(\sqrt{p(p-a)(p-b)(p-c)}\),其中 \(p=\frac{a+b+c}{2}\)
3. 分支结构程序设计
例 3-2
运算符优先级:
4. 循环结构程序设计
5. 数组与数据批量存储
P2615 [NOIP2015 提高组] 神奇的幻方
6. 字符串与文件操作
例 6-3
strcpy(s,"hello")
例 6-4
fgets(s,sizeof(s),stdin);
sscanf(s,"%d",&a);
sprintf(s,"%d",a);
例 6-7
char ch[10];
string s;
strcpy(ch,s.c_str()); // strncpy(ch,s.c_str(),10);
s = ch
P1598 垂直柱状图
值得一写
7. 函数与结构体
P1304 哥德巴赫猜想
哥德巴赫猜想:任一大于 \(2\) 的偶数都可以写成两个素数之和
二、初涉算法
8. 模拟与高精度
P1249 最大乘积
高精度的部分不再赘述,只考虑怎么分解
DP
取对数把乘积变成加法就可以用低精度背包得到决策
贪心
首先有 \(n(n+1)>(n-1)(n+2)\),据此不断调整可以得到答案为 \(2,3,\cdots,x,x+2,x+3,\cdots y\)
一种构造方法是找到最大的 \(\sum_{i=2}^{m}i\le n\),然后从 \(m\) 开始逐个 \(+1\) 至和为 \(n\)
P1045 [NOIP2003 普及组] 麦森数
\(2^{p}\) 的个位一定 \(\ne0\),所以 \(-1\) 很好处理
解方程 \(2^{p}=10^{q}\) 可以得到位数为 \(\lfloor p\lg2\rfloor+1\)
后 \(500\) 位可以高精快速幂,也可以高精乘低精(不压位,每次乘 \(2^{27}\))
9. 排序
P1116 车厢重组
冒泡排序交换相邻项次数 \(=\) 逆序对数
P1012 [NOIP1998 提高组] 拼数
10. 暴力枚举
P2241 统计方形(数据加强版)
矩形的数量为 \(\frac{n(n+1)m(m+1)}{4}\)
边长 \(i\) 的正方形的数量为 \((n-i+1)(m-i+1)\)
长方形数量为矩形数量减正方形数量
时间复杂度 \(O(n)\)
P1088 [NOIP2004 普及组] 火星人
next_permutation(begin(),end())
P1217 [USACO1.5] 回文质数 Prime Palindromes
回文数只有 \(O(\sqrt{b})\) 个,所以可以打表
11. 递推与递归
P1044 [NOIP2003 普及组] 栈
卡特兰数
P1028 [NOIP2001 普及组] 数的计算
递归的常数优于递推,因为不需要计算 \(\lfloor\frac{n}{2}\rfloor+1\cdots n-1\)
P1259 黑白棋子的移动
容易从 \(n\) 递归到 \(n-1\)
oo...oo**...**--
oo...o--*...**o*
oo...o***...--o*
从 \(n\ge4\) 可以猜出递归边界就是 \(n=4\),手玩出来
oooo****--
ooo--***o*
ooo*o**--*
o--*o**oo*
o*o*o*--o*
--o*o*o*o*
P1228 地毯填补问题
公主的存在使得基本图形是缺一个角的正方形
把大正方形均分成四个小正方形,递归填公主所在的小正方形,中间放一个地毯就使剩下三个小正方形也缺了一个角
12. 贪心
经典模型:
- P1223 排队接水
- P1803 凌乱的yyy / 线段覆盖
- P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
- P3817 小A的糖果
- P1106 删数问题
- P5019 [NOIP2018 提高组] 铺设道路
- P1094 [NOIP2007 普及组] 纪念品分组
- P4995 跳跳!
- P1080 [NOIP2012 提高组] 国王游戏
哈夫曼编码
P4447 [AHOI2018初中组] 分组
sol 1
二分答案
check 时从小到大尝试把连续 \(mid\) 个分到一组。分完后把剩下的数尝试接在已有组的后面,如果有数不能接在任意一组后面那么不可行
sol 2
从小到大考虑每个数,每组可以用 \((len,end)\) 表示(长度,最后一个数)
对于数 \(x\),把它接在 \(end=x-1\) 且 \(len\) 最小的组中,不能接到任意一组后面那么新建组
sol 3
13. 二分查找与二分答案
P1163 银行贷款
利率可能 \(>1\),二分需要注意上界
倍增代替二分答案:设置当前答案 \(x\) 和增量 \(k\)。如果 \(x+k\) 可行,那么令 \(x\leftarrow x+k, k\leftarrow 2k\),否则令 \(k\leftarrow\frac{k}{2}\)。\(k<\epsilon\) 时结束
14.搜索
数独
P1162 填涂颜色
sol 1
从边界的 \(0\) 开始搜,未被搜到的 \(0\) 就是在圈中的
sol 2
先按行数从小到大,再按列数从小到大,找到的第一个 \(1\) 的右下角一定是圈中的 \(0\),从这个 \(0\) 开始搜即可
三、简单数据结构
15. 线性表
P1449 后缀表达式
后缀表达式/逆波兰式:运算符在参数之后,严格从左到右计算(没有括号,不考虑优先级)
16. 二叉树
P1827 [USACO3.4] 美国血统 American Heritage
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
前序遍历的第一个位置是根,中序遍历中以根为分界分成左右子树
表达式树
叶子是参数,非叶子是运算符,自底向上用根的运算符合并子树的值就能得到表达式的值
若所有运算都是双目运算,则表达式树是二叉树。前/中/后序遍历是前/中/后缀表达式
P1229 遍历问题
前后序相同而中序不同只有一种情况:只有一个儿子的结点,该儿子位于左/右子树
问题转化为求只有一个儿子的结点数。易证 \(u\) 只有一个儿子的充要条件是前序存在 \(uv\) 且后序存在 \(vu\)
习题 16-3
P1185 绘制二叉树
设 \(i\) 层结点的满二叉树的根在 \(f[i]\) 列,叶子在 \(g[i]\) 行。观察可得 \(f[1]=1,f[2]=3,f[i]=2f[i-1],g[1]=1,g[i]=g[i-1]+f[i]-f[i-1]\)
容易得到需要的坐标/长度,模拟
17. 集合
18. 图的基本应用
P3916 图的遍历
建反图。从 \(n\) 到 \(1\) 枚举,标记能到达的点。每个点第一次被标记时就是答案
P2661 [NOIP2015 提高组] 信息传递
每个点只有一条出边意味着每个连通块中有且仅有一个环
P1127 词链
P1330 封锁阳光大学
每条边必须且仅能选一个端点,所以任意一条链都是 选-不选-选-不选 交替,有奇环则无解
因此一个连通块的答案是二分图左右部中较小的点数
P1363 幻象迷宫
显然如果当前棋盘和相邻棋盘都能走到 \((i,j)\) 那么 yes
把坐标模 \(n,m\) 转化为只在一个棋盘上走
一开始的想法是从 s 能走到环就 yes
。hack:相邻的四个棋盘形成一个环
5 5
#.#.#
..#..
#####
.S#..
#.#.#
正解是记录走到 \((i,j)\) 能否走到,如果两次走到 \((i,j)\) 且模之前的坐标不同那么 yes
四、基础数学与数论
19. 位运算与进制转换
负数的二进制
反码:把绝对值的二进制取反
补码:反码 \(+1\)。全 \(0\) 表示 \(0\),全 \(1\) 表示 \(-1\)
减法:
小数的二进制
逻辑命题
徳·摩根定律:\(\overline{A}+\overline{B}=\overline{AB},\overline{A}\cdot\overline{B}=\overline{A+B}\)
实现了一定条件下与和或的转化
P1017 [NOIP2000 提高组] 进制转换
\(n=a(-R)+b\) 的思路不变
被除数 \(=\) 商 \(\times\) 除数 \(+\) 余数,把商 \(+1\) 就能把余数调成正数
20. 计数原理与排列组合
P1866 编号
限制关系是包含的,从强到弱考虑
P2789 直线交点数
设 \(f[i,j]\) 表示 \(i\) 条直线形成 \(j\) 个交点是否可行,转移枚举接下来 \(k\) 条直线平行(不与已有的直线平行),新增 \(ik\) 个交点
bitset
优化,时间复杂度 \(\displaystyle O(\frac{n^{4}}{\omega})\)
21. 整除理论
P1414 又是毕业季II
考虑每个值是多少个数的约数
P2651 添加括号III
\(a[1]\) 肯定是分子,\(a[2]\) 肯定是分母,构造 \(a[1]/(a[2]/a[3]/\cdots/a[n])\),仅有 \(a[2]\) 是分母。判断能否将 \(a[2]\) 约分为 \(1\) 即可
P2660 zzc 种田
设宽为 \(n\),长为 \(m\) 的答案为 \(4f(n,m)\)
结论:每次删边长为 \(n\) 的正方形最优,即 \(f(n,m)=f(n,m-n)+n\)
边界是 \(f(0,\gcd(n,m))=0\),所以 \(f(n,m)=n+m-\gcd(n,m)\)
反证:如果所有正方形边长都 \(<n\),那么两条长边由不同的正方形覆盖,代价至少是 \(2m\)