「洛谷 NOIP 计划 2021」【学习1】降维技巧

题单内容

虽然计划还没有正式开始,但是考虑到有些同学比较闲,所以就提前给出自学内容。

请同学尽可能完成以下内容,没有很难,也不涉及特定的算法。遇到困难请在群里讨论。

  • 前缀和-差分(区间转前缀)
    • 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 提高组] 天天爱跑步
  • 离散化(压缩一维的大小)
    • 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)) 的区间和操作 .

具体为令

[S_n=sum_{i=1}^na_i=S_{n-1}+a_n ]

则区间和 (a_l+cdots+a_r=S_r-S_{l-1}) .

C++ 标准库中实现了前缀和函数 std::partial_sum,定义于头文件 <numeric> 中。

二维前缀和

多维前缀和的普通求解方法几乎都是基于容斥原理 .

考虑一个二维数组,求子矩形和 .

类似,令

[S_{x,y}=sum_{i=1}^xsum_{j=1}^y a_{i,j} ]

用容斥原理可 (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];

求解高维前缀和的核心思想也就是逐维处理,可以类比二维前缀和的求法稍微模拟一下 .

[f_igets f_i+f_{ioplus(1mathrm{\, shl\,}j)} ]

的写法也是因为 (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)).

显然:

  1. (d_i) 的前缀和就是 (a_i) .
  2. 对于 (d_i) 上一点的扰动(如 (+1)),会引起前缀和后数组(原 (a_i))的 (i) 点后每一点均作同样扰动 .

因为第二条性质,使得它可以求解若干次区间加和最后一次询问操作后序列的操作 .

C++ 标准库中实现了差分函数 std::adjacent_difference,定义于头文件 <numeric> 中。

树⭐上⭐差⭐分

树上差分,用于解决如下问题:

(q) 条路径,起点 (s_i) 终点 (t_i),问每个点被多少个路径包含 .

只需要进行

[d_sgets d_s+1 ]

[d_tgets d_t+1 ]

[d_{lca}gets d_{lca}-1 ]

[d_{fa(lca)}gets d_{fa(lca)}-1 ]

然后作个树上前缀和即可 .

要维护每条边被多少个路径包含也类似,相当于把每条边下放到下面的点上了,可以参考树上前缀和部分 .

离散化

普通离散化

排序去重 lower_bound

多维离散化

一维一维离散化

双指针

普通双指针

两个指针,一般都在一段或两端各一个

找出单调性,跳指针即可

尺取法

一般是枚举一维,另一位在前一次基础上枚举 .

单调栈/单调队列

单调栈

维护单调性区块用

单调队列

维护单调区间最值用 .

原文地址:https://www.cnblogs.com/CDOI-24374/p/14987362.html