题单内容
虽然计划还没有正式开始,但是考虑到有些同学比较闲,所以就提前给出自学内容。
请同学尽可能完成以下内容,没有很难,也不涉及特定的算法。遇到困难请在群里讨论。
- 前缀和-差分(区间转前缀)
- P1115 最大子段和
- P3406 海底高铁
- P2671 [NOIP2015 普及组] 求和
- P1314 [NOIP2011 提高组] 聪明的质监员
- P1083 [NOIP2012 提高组] 借教室
- P2882 [USACO07MAR]Face The Right Way G
- 高维前缀和
- P2004 领地选择
- P3397 地毯
- P2280 [HNOI2003]激光炸弹
- P1719 最大加权矩形
- P3017 [USACO11MAR]Brownie Slicing G
- AT4168 [ARC100C] Or Plus Max
- 树上差分
- 按 DFS 栈序
- P3128 [USACO15DEC]Max Flow P
- P3258 [JLOI2014]松鼠的新家
- P2680 [NOIP2015 提高组] 运输计划
- 按子树序
- P1600 [NOIP2016 提高组] 天天爱跑步
- 按 DFS 栈序
- 离散化(压缩一维的大小)
- P1097 [NOIP2007 提高组] 统计数字
- P1955 [NOI2015] 程序自动分析
- P1052 [NOIP2005 提高组] 过河
- 多维离散化
- P3138 [USACO16FEB]Load Balancing S
- 双指针(关于两维的询问,通过单调性降维)
- 普通双指针
- P1102 A-B 数对
- P3143 [USACO16OPEN]Diamond Collector S
- P4653 [CEOI2017]Sure Bet
- P1311 [NOIP2011 提高组] 选择客栈
- 尺取法
- P1638 逛画展
- UVA11572 唯一的雪花 Unique Snowflakes
- AT4142 [ARC098B] Xor Sum 2
- P1083 [NOIP2012 提高组] 借教室
- CF939E Maximize!
- 普通双指针
- 单调栈/单调队列(应用:单调性优化 DP)
- 单调栈
- P2866 [USACO06NOV]Bad Hair Day S
- P3467 [POI2008]PLA-Postering
- P1950 长方形
- 单调队列
- P1886 滑动窗口 /【模板】单调队列
- P2032 扫描
- P2880 [USACO07JAN]Balanced Lineup G
- P2251 质量检测
- P1816 忠诚
- P1440 求m区间内的最小值
- P2216 [HAOI2007]理想的正方形
- P1714 切蛋糕
- P1725 琪露诺
- P2627 [USACO11OPEN]Mowing the Lawn G
- P2034 选择数字
- P3957 [NOIP2017 普及组] 跳房子
- P2569 [SCOI2010]股票交易
- P3800 Power收集
- P2254 [NOI2005] 瑰丽华尔兹
- P1419 寻找段落
- 单调栈
前缀和-差分(区间转前缀)
前缀和 (Prefix Sum)
普通前缀和
前缀和可以通过 (O(n)) 的预处理实现 (O(1)) 的区间和操作 .
具体为令
则区间和 (a_l+cdots+a_r=S_r-S_{l-1}) .
C++ 标准库中实现了前缀和函数 std::partial_sum
,定义于头文件 <numeric>
中。
二维前缀和
多维前缀和的普通求解方法几乎都是基于容斥原理 .
考虑一个二维数组,求子矩形和 .
类似,令
用容斥原理可 (O(n^2)) 递推求出 .
而子矩形和也可通过容斥原理求出(左下角 ((x_1,y_1)),右上角 ((x_2,y_2)) 的答案是 (S_{x_2,y_2}-S_{x_1-1,y_2}-S_{x_2,y_1-1}+S_{x_1-1,y_1-1})) .
画个图就能很容易理解啦 qwq
高维前缀和
高维前缀和一般解决这样的问题:
对于所有 (0le ile 2^n-1),求
[sum_{jsubset i}a_j ]其中 (jsubset i) 当且仅当 (j) 的二进制表示为 (i) 的二进制表示的子集 .
枚举子集(暴力)求的复杂度是 (O(3^n)) 的,但高维前缀和后就是 (O(n2^n)) 的了 .
先放代码:
for (int j=0;j<n;j++)
for (int i=0;i<1<<n;i++)
if (i>>j&1) a[i]+=a[i^(1<<j)];
原因:
先从二维前缀和说起 .
一般写的二维前缀和的预处理是这么写的:
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
但其实这种办法并不利于拓展,更好的写法是 逐维前缀和,如下:
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]+=a[i-1][j];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]+=a[i][j-1];
(这里直接在原数组进行是为了展示一致性)
类似的,高维前缀和均可这么做,例如 (n) 维前缀和可以这样:
for (int i1=1;i1<=n1;i1++)
for (int i2=1;i2<=n2;i2++)
...
for (int in=1;i1<=nn;in++)
a[i1][i2][...][in]+=a[i1+1][i2][...][in];
for (int i1=1;i1<=n1;i1++)
for (int i2=1;i2<=n2;i2++)
...
for (int in=1;i1<=nn;in++)
a[i1][i2][...][in]+=a[i1][i2+1][...][in];
... ...
for (int i1=1;i1<=n1;i1++)
for (int i2=1;i2<=n2;i2++)
...
for (int in=1;i1<=nn;in++)
a[i1][i2][...][in]+=a[i1][i2][...][in+1];
求解高维前缀和的核心思想也就是逐维处理,可以类比二维前缀和的求法稍微模拟一下 .
的写法也是因为 (ioplus(1mathrm{\, shl\,}j)) 是 (i) 的下一层 .
树上前缀和
树上前缀和主要用于计算路径上的 点权/边权 和
设 (S_i) 表示 (i) 到根的路径上的 点权/边权 和,这可以一遍 dfs 求出 .
然后:
- 若是点权,(u,v) 路径上的和为 (S_u+S_v-S_{lca}-S_{fa(lca)})
- 若是边权,(u,v) 路径上的和为 (S_u+S_v-2S_{lca})
其中 (fa(u)) 表示 (u) 的父亲节点,(lca=operatorname{LCA}(u,v)) 为 (u,v) 的最近公共祖先 .
差分 (Difference)
普通差分
差分是一种和前缀和相对的策略,可以当做是求和的逆运算 .
即令 (d_i=a_i-a_{i-1})(认为 (a_0=0)).
显然:
- (d_i) 的前缀和就是 (a_i) .
- 对于 (d_i) 上一点的扰动(如 (+1)),会引起前缀和后数组(原 (a_i))的 (i) 点后每一点均作同样扰动 .
因为第二条性质,使得它可以求解若干次区间加和最后一次询问操作后序列的操作 .
C++ 标准库中实现了差分函数 std::adjacent_difference
,定义于头文件 <numeric>
中。
树⭐上⭐差⭐分
树上差分,用于解决如下问题:
有 (q) 条路径,起点 (s_i) 终点 (t_i),问每个点被多少个路径包含 .
只需要进行
然后作个树上前缀和即可 .
要维护每条边被多少个路径包含也类似,相当于把每条边下放到下面的点上了,可以参考树上前缀和部分 .
离散化
普通离散化
排序去重 lower_bound
多维离散化
一维一维离散化
双指针
普通双指针
两个指针,一般都在一段或两端各一个
找出单调性,跳指针即可
尺取法
一般是枚举一维,另一位在前一次基础上枚举 .
单调栈/单调队列
单调栈
维护单调性区块用
单调队列
维护单调区间最值用 .