题意
给出一个数组,要求有多少对(l,r),使得a[l]...a[r] 之间无重复元素,且max(al,al+1,…,ar)−(r−l+1)≤k
思路
启发式合并,st表处理出每个区间最大值所在位置,然后求出每个点不重复的左右区间。
暴力扫描短的一半计数。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e6 + 10;
int t, n, k, a[N], st[N][20], le[N], ri[N], vis[N];
ll ans;
int qmax(int l, int r) {
int k = log2(r - l + 1);
if(a[st[l][k]] > a[st[r - (1 << k) + 1][k]])
return st[l][k];
return st[r - (1 << k) + 1][k];
}
void solve(int l, int r) {
if(r < l)
return;
int p = qmax(l, r);
int len = a[p] - k;
if(p - l < r - p) {
for(int i = p; i >= l; i--) {
int L = max(p, i + len - 1);
int R = min(ri[i], r);
if(R >= L)
ans += R - L + 1;
}
} else {
for(int i = p; i <= r; i++) {
int L = max(l, le[i]);
int R = min(i - len + 1, p);
if(R >= L)
ans += R - L + 1;
}
}
solve(l, p - 1);
solve(p + 1, r);
}
int main() {
scanf("%d", &t);
while(t--) {
ans = 0;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), st[i][0] = i;
memset(vis, 0, sizeof vis);
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i <= n; i++)
if((i + (1 << (j - 1))) > n || a[st[i][j - 1]] > a[st[i + (1 << (j - 1))][j - 1]])
st[i][j] = st[i][j - 1];
else
st[i][j] = st[i + (1 << (j - 1))][j - 1];
for(int i = 1; i <= n; i++) {
le[i] = vis[a[i]] + 1;
vis[a[i]] = i;
}
for(int i = 1; i <= n; i++)
vis[i] = n + 1;
for(int i = n; i > 0; i--) {
ri[i] = vis[a[i]] - 1;
vis[a[i]] = i;
}
for(int i = 2; i <= n; i++)
le[i] = max(le[i - 1], le[i]);
for(int i = n - 1; i > 0; i--)
ri[i] = min(ri[i + 1], ri[i]);
solve(1, n);
printf("%lld\n", ans);
}
return 0;
}