说实话这题挺简单的(知道题解以后),但是一般人做的时候脑洞不大一点还不一定做得出来。
显然,题目可化简为:给定 N 个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。
进一步可以化为:有多少种情况使得,相邻的 k 个数中最大值和最小值的差小于等于 k-1。
大致有两种解法,一种是分治,一种是线段树。
这里主要讲一下分治的解法。
考虑分治,对于当前区间[L,R],记区间中点为 mid。当前区间的答案就是Ans[L..mid]+Ans[mid+1..R]+跨过中点的合法区间数,然后就分为两种情况了:
1.最小值和最大值在同侧。
2.最小值和最大值在异侧。
下面只考虑最值同在左,和最小值在左,最大值在右的情况。
因为其余两种是对称的。
对于最值同在左侧的情况,我们O(N)枚举左边界在哪,然后可以
计算出右边界的位置,在判断是否合法,统计答案。对于最小值在左侧,最大值在右侧的情况,如果一个区间满足我们所要求的关系的话,就一定有:
max(a[mid +1]…a[right]) - min(a[left]…a[mid]) =right- left
移项可得
max(a[mid +1]…a[right]) - right = min(a[left]…a[mid]) -left
然后可以用单调栈来完成这个任务。 如果加一些黑科技可以大大减少代码量,但是复杂度会多一个 log。
简单提一下,线段树解法的思路大致也是维护一个单调栈,然后进行区间修改和查询,统计答案。
蒟蒻还在用Pas,虽然偶尔也用C艹
uses math;
const maxn=300010;
var
n,i,j,k,p,m,x:longint;
maxm,minm,num:array[0..maxn]of longint;
f:array[