题目大意:初始有一个序列 a,定义 a 的一个子序列是好序列:若
[
l
,
r
]
[l,r]
[l,r]内
m
a
x
(
a
l
,
a
l
+
1
,
.
.
,
a
r
)
−
(
r
−
l
+
1
)
≤
k
max(a_l,a_{l + 1},..,a_r) - (r - l + 1) \leq k
max(al,al+1,..,ar)−(r−l+1)≤k 且
a
l
,
a
l
+
1
,
.
.
.
,
a
r
a_l,a_{l + 1},...,a_r
al,al+1,...,ar元素不重复。让你求 a 序列中有多少好序列。
做法:先考虑无元素不重复限制的做法:处理出每个元素左边一个比他大和右边第一个比他大,由于区间长度必须大于 (max - k),对于当前每个处理元素:可以枚举左端点,计算有多少合法的右端点,然后统计答案,因为没有限制,只要长度超过 (max - k),且覆盖了当前处理元素的右端点都是合法的。
考虑有元素不能重复这个限制,前面过程一样,考虑统计答案时,枚举左端点 i,长度必须大于 x,因此右端点最靠左的位置为 i + x - 1,右端点最靠右的位置不再是当前元素作为最大值的右区间,而是以 i 为起点,向右走元素不重复的情况下能走到的最大位置(需要多一个预处理),处于这个范围内的右端点是合法的右端点。
所以具体做法是:先预处理出以每个位置为起点,向右走元素不重复的情况下的能走到的最大的位置,为了保证复杂度是
n
l
o
g
n
nlogn
nlogn,还可以处理出以每个位置为起点向左走能走的最小位置。这样可以在统计答案时枚举右端点计算有多少合法的左端点,选择较短的一边来枚举统计答案,从而保证复杂度不会退化。
关于统计答案,可以用分治,也可以用单调栈处理出每个值作为最大值时的左端点和右端点,然后扫一遍。复杂度都将是
n
l
o
g
n
nlogn
nlogn,只能优化常数
以下是单调栈代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
typedef long long ll;
int t,n,k;
int L[maxn],l[maxn],R[maxn],r[maxn],nxt[maxn],lst[maxn];
int a[maxn];
int sta[maxn],top;
ll res = 0;
void init() {
res = top = 0;
fill(L,L + n + 1,0);
fill(R,R + n + 1,0);
for(int i = 0; i <= n + 1; i++) {
l[i] = lst[i] = 0;
r[i] = nxt[i] = n + 1;
}
}
ll solve(int p) {
ll ans = 0;
int mid = L[p] + R[p] >> 1;
int x = a[p] - k;
if(p <= mid) {
for(int i = L[p] + 1; i <= p; i++) {
int lp = max(p,i + x - 1);
int rp = min(R[p] - 1,r[i] - 1);
if(rp >= lp) ans += (rp - lp + 1);
}
}
else {
for(int i = p; i < R[p]; i++) {
int rp = min(p,i - x + 1);
int lp = max(L[p] + 1,l[i] + 1);
if(rp >= lp) ans += (rp - lp + 1);
}
}
return ans;
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&k);
init();
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
for(int i = 1; i <= n; i++) {
l[i] = max(l[i - 1],lst[a[i]]);
lst[a[i]] = i;
while(top > 0 && a[sta[top]] <= a[i]) {
R[sta[top]] = i;
top--;
}
sta[++top] = i;
}
while(top > 0) {
R[sta[top]] = n + 1;
top--;
}
for(int i = n; i >= 1; i--) {
r[i] = min(r[i + 1],nxt[a[i]]);
nxt[a[i]] = i;
while(top > 0 && a[sta[top]] <= a[i]) {
L[sta[top]] = i;
top--;
}
sta[++top] = i;
}
while(top > 0) {
L[sta[top]] = 0;
top--;
}
for(int i = 1; i <= n; i++)
res += solve(i);
printf("%lld\n",res);
}
return 0;
}