zoukankan      html  css  js  c++  java
  • 「洛谷 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

    多维离散化

    一维一维离散化

    双指针

    普通双指针

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

    找出单调性,跳指针即可

    尺取法

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

    单调栈/单调队列

    单调栈

    维护单调性区块用

    单调队列

    维护单调区间最值用 .

  • 相关阅读:
    Linux如何编译安装源码包软件
    安装python发行版本,并用conda来管理Environments,Python,packages
    命令远程传输文件
    CentOS7更换yum源为阿里云镜像源
    CentOS安装pip并修改源为豆瓣源
    查看SELinux状态及关闭SELinux
    解决CentOS查询不到ip
    chmod变更文件或目录的权限
    开启HTML5之旅。。。
    JDK 动态代理的实现
  • 原文地址:https://www.cnblogs.com/CDOI-24374/p/14987362.html
Copyright © 2011-2022 走看看