解题报告 (十) 单调栈



单调栈 解题报告

PKU 2082 Terrible Sets

链接:PKU 2082 Terrible Sets
题意:把矩形在x轴上排列好以后就是求最大内接子矩形的问题了。

HDU 2430 Beans

链接:HDU 2430 Beans
题意:给定 n ( n < = 1 0 6 ) n(n <=10^6) n(n<=106) 个数字 a [ i ] a[i] a[i] 和两个数字 K K K P ( 0 < = P < K ) P(0 <= P < K) P(0<=P<K),要求求一个区间 ( i , j ) (i,j) (i,j),使得区间和最大,并且区间里的数对 K 取模小于等于 P。求最大的 s u m ( i , j ) / K sum(i,j) / K sum(i,j)/K

  • 题解:前缀和 + 单调栈
  • 1)首先把所有的数求出前缀和以后对 K K K 取模。得到前缀和数组 s u m [ i ] sum[i] sum[i] 代表前 i i i 项的和(为了方便,下标从1开始)。
  • 2)令区间 ( i , j ) (i,j) (i,j) 的和模上 K K K 的值是: M ( i , j ) = ( s u m [ j ] − s u m [ i − 1 ] ) m o d    K M(i,j) = (sum[j] - sum[i-1])\mod K M(i,j)=(sum[j]sum[i1])modK 我们需要做的就是对于任意一个 j j j,要让 i i i 的值尽量小(这样区间才能尽量长),并且 M ( i , j ) < = P M(i,j) <= P M(i,j)<=P
  • 3)枚举 j j j,然后转化成线段树找区间最值问题。

HDU 4252 A Famous City

链接:HDU 4252 A Famous City
题意:给定一个直方图区域,问最少用多少的矩形能把这些区域填满。直方图的每个高度按照 h [ i ] ( 1 < = i < = n ) h[i] (1 <= i <= n) h[i](1<=i<=n) 的形式给出。 一种解决方案如下:答案为 8 。在这里插入图片描述

  • 题解:考虑如下三种情况:
  • 1)如果整个图是单调递增的,即 h [ i − 1 ] < h [ i ] h[i-1] < h[i] h[i1]<h[i],那么需要的矩形数就是不同高度的数目;
  • 2)如果中间有一次出现 h [ i − 1 ] > h [ i ] h[i-1] > h[i] h[i1]>h[i],那么就要考虑 h [ i ] h[i] h[i] 能否和之前出现过的相同高度合并(如上图橙色方柱所示);
  • 3)如果 h [ i − 1 ] = = h [ i ] h[i-1] == h[i] h[i1]==h[i],那么 h [ i ] h[i] h[i] 没有任何作用;
  • 基于以上性质,用一个单调栈来维护一个单调递增序列,对 栈顶元素 t o p top top 和 当前元素 i i i 进行高度判定如下:
  • 1)栈为空或者 h [ t o p ] < h [ i ] h[top] < h[i] h[top]<h[i],如果 h [ i ] > 0 h[i] > 0 h[i]>0 则入栈;
  • 2)栈非空并且 h [ t o p ] = = h [ i ] h[top] == h[i] h[top]==h[i],不作处理;
  • 3)栈非空并且 h [ t o p ] > h [ i ] h[top] > h[i] h[top]>h[i],不断弹出元素,并且计数器累加;当栈为空或者出现 h [ t o p ] < h [ i ] h[top] < h[i] h[top]<h[i],和 1)判定类似, h [ i ] > 0 h[i] > 0 h[i]>0 则入栈;
  • [ 注意:为了让所有元素都能出栈,元素末尾加入一个 -1]

PKU 2796 Feel Good

链接:PKU 2796 Feel Good
题意:给定 n n n 个数 a [ i ] ( 0 < = a [ i ] < = 1 0 6 ) a[i] (0 <= a[i] <= 10^6) a[i](0<=a[i]<=106),令 s u m ( i , j ) = ∑ k = i j a [ k ] sum(i,j) = \sum_{k=i}^j a[k] sum(i,j)=k=ija[k],求 s u m ( i , j ) ∗ m i n a ( i , j ) sum(i,j)*min_a(i,j) sum(i,j)mina(i,j) 的最大值。

  • 沿用 HDU 1506 Largest Rectangle in a Histogram 的思想,用单调栈维护一个单调不降序列,预处理前缀和。注意有 0 的情况,所以最先的候选最大值不能设定为 0,要定为小于零的数。

HDU 3410 Passing the Message

链接:HDU 3410 Passing the Message
题意:给定 n n n 个数 a [ i ] ( 0 < = a [ i ] < = 1 0 5 ) a[i] (0 <= a[i] <= 10^5) a[i](0<=a[i]<=105),求每个数左边比它小最大的数,以及每个数右边比它小最大的数。

  • 题解:单调递减栈
  • 两边分别扫描,然后维护一个单调递减的栈即可;

HDU 6759 Leading Robots

链接:HDU 6759 Leading Robots
题意:给定 n ( n < = 50000 ) n( n <= 50000) n(n<=50000) 个人的起始位置 s s s 和加速度 a a a,每个人都做匀加速运动,问有多少个人能在某个时刻成为 “领头羊” (即没有任何人能与之比肩)。

  • 需要考虑的几个思路:
  • 1)每个人的位移曲线是一条相对于 y y y 轴对称的抛物线,且只在第一象限;
  • 2)任意两条抛物线要么重合,要么只可能存在一个交点,换言之,如果起始位置一开始落后,那么超越的唯一机会就是加速度比起始位置大于他的人都大;
  • 3)起始位置相同的两个人,加速度小的那个永远没有机会成为 “领头羊”;
  • 4)起始位置相同,加速度也相同的两个人都没有机会成为 “领头羊”;
  • 对于两个人 R i R_i Ri R j R_j Rj,满足 ( s i > s j 且 a i < a j ) (s_i > s_j 且 a_i < a_j) (si>sjai<aj),则 a j a_j aj 追上 a i a_i ai 的时间为: t > = 2 ( s i − s j ) a j − a i t >= \sqrt {\frac {2(s_i - s_j)}{a_j - a_i}} t>=ajai2(sisj)
  • 考虑三个人, R 1 = ( s 1 , a 1 ) , R 2 = ( s 2 , a 2 ) , R 3 = ( s 3 , a 3 ) R_1 = (s_1, a_1),R_2 = (s_2, a_2),R_3 = (s_3, a_3) R1=(s1,a1)R2=(s2,a2)R3=(s3,a3),并且满足 ( s 1 > s 2 > s 3 ) (s_1 > s_2 > s_3) (s1>s2>s3) R 2 R_2 R2 想要追上来的必要条件是 ( a 2 > a 1 ) (a_2 > a_1) (a2>a1) R 3 R_3 R3 想要追上来的条件是 ( a 3 > m a x ( a 1 , a 2 ) ) (a_3 > max(a_1,a_2)) (a3>max(a1,a2))。但是这样还不够,试想一下,如果 R 3 R_3 R3 追上 R 2 R_2 R2 的时间早于 R 2 R_2 R2 追上 R 1 R_1 R1 的时间,那么 R 2 R_2 R2 就再也没有机会成为第一了。
  • 那么,我们对于一个位置递减,加速度递增的序列,我们需要维护一个 “追上时间” 单调递增的序列;
  • 算法设计如下:
  • 1)首先对数据进行排序,按照起始位置作为第一关键字递减,位置相同则加速度递减;
  • 2)对位置相同,加速度小元素直接去掉;对两个关键字都相同的元素进行哈希标记,不作为 “领头羊” 候选人;
  • 3)对数据进行从前往后的扫描,记录当前加速度最大值为 M a x a Max_a Maxa,对于所有扫描到的加速度小于等于 M a x a Max_a Maxa 的进行跳过处理,每次扫描完毕更新加速度最大值:
  • 3.a)如果栈中小于两个元素,则入栈当前元素;
  • 3.b)否则计算栈顶两个元素的 “追上时间” t t o p t_{top} ttop,以及栈顶元素和当前元素的 “追上时间” t c u r t_{cur} tcur,如果 t t o p > = t c u r t_{top} >= t_{cur} ttop>=tcur,则弹出栈顶元素, 计算完毕将当前元素入栈;
  • 扫描完毕后,将所有栈顶元素出栈,并且没有被 哈希标记 的人就是能够在某个时刻成为 “领头羊” 的人;
  • [ 注意:计算时间的时候 开方 + 除法 会引起精度误差,比较的时候可以转换成乘法来操作]

HDU 5033 Building

链接:HDU 5033 Building
题意:给定 n ( n < = 1 0 5 ) n(n <= 10^5) n(n<=105) 个建筑的 x [ i ] x[i] x[i] 坐标和高度 h [ i ] h[i] h[i],然后 Q ( Q < = 1 0 5 ) Q(Q <= 10^5) Q(Q<=105) 次询问,问在某个位置 p p p 需要多少的仰角才能看到天空;

  • 题解:离线算法 + 单调栈的斜率优化
  • 对于任何一个 p p p 都要考虑左右两边的情况,但是左右两边的情况处理起来是一样的,所以这里只考虑左边;然后进行逆序以后再算一次,再把两个和 x 轴的角度相加,用 180度 去减就行。那么有以下几个注意点:
  • 1)对于某个 p p p 位置,位于它左边的建筑,随着 x x x 递增, h h h 一定是单调递减的,不然肯定不会成为阻碍视角的最高仰角的那个建筑。
  • 2)一旦出现三个高度单调递减的建筑,它们的形状组成一个下凹的形状,那么中间的那个建筑,一定是不可能成为阻碍视角的建筑,所以可以认为中间的建筑不存在(可以随便找个的坐标轴上的点往中间的建筑做连线,一定是交在第一个建筑或者第三个建筑上);
  • 3)于是对于建筑序列,我们只需要维护一个相邻建筑逐渐变陡峭的序列(表现在数值上就是斜率逐渐往负的方向越来越大),可以用单调栈来维护这个序列;
    在这里插入图片描述
  • 4)对于任意一个位置 p p p,现在要找到那个影响它视野的建筑,可以检查栈中的建筑,令栈顶两个建筑组成的斜率为 K t o p K_{top} Ktop,栈顶建筑和 p p p 位置组成的斜率为 K p K_p Kp,如果 K t o p < = K p K_{top} <= K_p Ktop<=Kp(注意斜率恒为负) ,则出栈,继续判断;否则,即找到了那个阻碍它视野的建筑;

HDU 4923 Room and Moor

链接:HDU 4923 Room and Moor
题意:给定 n ( n < = 100000 ) n(n <= 100000) n(n<=100000) 个元素的序列 A,只由 0 和 1 组成,求一个非递减序列 B,满足如下条件:

  • 题意:单调栈
  • 首先把所有数分段拆成如下这样的段:111000, 11100, 110, 1111110,即都是有 1 开头, 0结尾,那么对于某一段,假设 1 的个数为 a a a,0 的个数为 b b b,令选择的的 B i = x B_i = x Bi=x,则有差的平方和为: a ( x − 1 ) 2 + b x 2 a(x-1)^2 + bx^2 a(x1)2+bx2 利用抛物线的极值可以知道,当 x = a a + b x = \frac {a} {a + b} x=a+ba 时取得最小值。
  • 如果这样求出来的 B i B_i Bi 都能够单调不降,那么问题的解也就出来了;
  • 当出现 B i > B i + 1 B_i > B_{i+1} Bi>Bi+1 的时候,我们可以选择将 B i B_i Bi B i + 1 B_{i+1} Bi+1 进行合并,用单调递增的栈来维护这个序列,知道栈中的元素都是单调递增位置。

PKU 3250 Bad Hair Day

链接:PKU 3250 Bad Hair Day
题意: n ( n < = 1 0 5 ) n(n <= 10^5) n(n<=105) 个人从左往右顺序排列,给定他们的高度 h [ i ] h[i] h[i],,第 i i i 个人往右看能够看到比他高度低的人的个数记为 s [ i ] s[i] s[i],求 ∑ i = 1 n s [ i ] \sum_{i=1}^{n} s[i] i=1ns[i]

  • 我们考虑下图的两种情况:
  • 1)如下图所示,当前的人编号为 c c c a a a b b b 呈逆序排列,那么 a a a b b b 的解都是 c c c
  • 2)如下图所示,当前的人编号为 c c c a a a b b b 呈顺序排列(或者 a 和 b 高度相等),那么只有 b b b 的解是 c c c;在这个问题中我们发现, 当求 b b b 的解时, a a a 的解已经求出来了,不再需要;
  • a a a b b b 理解成栈中的元素,我们首先在序列末尾插入一个无限大的数,然后维护一个单调递减的栈,从左往右枚举每个人的高度 h [ i ] h[i] h[i],然后取 栈顶元素高度 和 当前高度 比较,如果 栈顶元素高度 <= 当前高度,代表栈顶的那个人能够看到的最远的人就是当前第 i i i 个人,则累加答案,并且出栈,迭代循环处理,直到 栈顶元素高度 > 当前高度 或者 栈空,再将 i i i 入栈;
  • 如图,红色方柱为 h [ i ] h[i] h[i],绿色方柱均为本次处理要出栈的元素,蓝色方柱为处理完还在栈中的元素,栈中元素始终保持单调递减。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值