连号区间数+CF526F[CF]
CF题目链接
题意
- 【CF题意】:在N*N的二维矩阵上有N个点,一定是一行或者一列,有且仅有一个数,总有多少个子矩阵,也满足一行或一列,有且仅有一个数,N为30万.
- 【蓝桥杯题意】:给出N个数的某个排列,求出有多少个子序列,把它排序后是连续递增的.N为5万.
题外话
这是一个两年前就折磨了我的题目..本来我已经忘了这事儿,没想到前几天在某群里看见有人问这个问题了,一下子勾起了我的回忆.在13年的蓝桥杯省赛中,最后一道压轴题就是连号区间数,当时写了个$O(n^2)$的算法水了一下,后来在蓝桥杯官网中也发现数据比较水,N方随便过,我只知道这个算法是不科学的,但想了好久,也没搞定这题,后来渐渐的就忘了..
前两天看见有人问起这题,说是线段树是一个科学的解法,还给了一个题解链接,于是我突然又兴奋了,决定解决掉这题…
正题
- 主要算法:dp & 线段树
思路
- 对于
A[1...N]
的一个子序列A[j...i](i >= j)
,令区间内最大值maxValue = max(A[j...i])
,最小值minValue = min(A[j...i])
可以得到一个公式maxValue - minValue >= i - j
,当maxValue - minValue == i - j
的时候,我们的答案+1,把j
移到左边转换一下就成了maxValue - minValue + j == i
;
所以我们维护一下等式左边的值dp[p]
,当我们从左到右枚举A[i]
,看有多少个位置p的结果等于k就可以得出答案了(p<=i)
,
怎么维护呢? - 我们设函数
f(j) = maxValue - minValue + j
(注意maxValue和minValue一直在变).
我们可以用一棵线段树来维护,每个节点seg[k]
范围为l到r,表示的是枚举到i时,l到r范围内有多少个最小值fMinLR,fMinLR = min(f(j)),(l <= j <= r)
我们初始时所有叶子节点都为对应的位置p - 对于第i个数
A[i]
,我们可以找到一个j满足:j要尽量小,而且A[j - 1] < A[i]
,找到一个k满足:k要尽量小而且A[k - 1] > A[i]
, 这时候区间[j,i]的最大值maxValue等于A[i],区间[k, i]的最小值minValue是A[i],所以这时候区间更新(区间+maxValue,区间-minValue)就可以得出区间内所有的f(x)值了
枚举完A[i]该枚举下一个了A[i + 1],会有新的区间[j1,i + 1],[k1, i + 1],更新新区间的时候会和这次的更新重叠,所以要把这次更新的恢复回去(重叠部分),然后才能进行下次更新。
差不多就这样了…
|
|