题意:
给定一个长度为 n 的数组,求满足 mx - mn + 1 = cnt 的区间个数,其中 mx、mn 为区间最值,cnt 为区间数字种类。 (n <= 1e5)
链接:
https://cn.vjudge.net/problem/Gym-102222L
题解:
移项得到 mx - mn - cnt = -1,令 T[L, R] = mx - mn - cnt,可每次固定区间右端点R,求满足 T[L, R] = -1 (1 <= L <= R) 的左端点L个数,注意到 T[L, R] >= -1 且 T[R, R] = -1,考虑在每个R时,用线段树维护 T[L, R] (1 <= L <= R) 最小值,贡献为最小值个数。
接下来考虑 R - 1 -> R 时的维护,对于 cnt 只需找到 A[R] 上一出现位置last,线段树区间 [last + 1, R] -1 即可;对于 mx、mn 的维护,只需找到 A[R] 作为极值的左端点L,相应进行区间加减操作,可用单调栈维护。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
map<int, int> mp;
int mn[maxn << 2], num[maxn << 2], add[maxn << 2];
pii smx[maxn], smn[maxn];
int a[maxn], n, p1, p2;
void build(int l, int r, int rt){
mn[rt] = add[rt] = 0;
num[rt] = r - l + 1;
if(l == r) return;
int mid = gmid;
build(l, mid, lson);
build(mid + 1, r, rson);
}
void pushUp(int rt){
mn[rt] = min(mn[lson], mn[rson]);
if(mn[lson] == mn[rson]) num[rt] = num[lson] + num[rson];
else if(mn[lson] < mn[rson]) num[rt] = num[lson];
else num[rt] = num[rson];
}
void pushDown(int rt){
if(add[rt]){
add[lson] += add[rt], add[rson] += add[rt];
mn[lson] += add[rt], mn[rson] += add[rt];
add[rt] = 0;
}
}
void update(int l, int r, int rt, int L, int R, int val){
if(l >= L && r <= R){
mn[rt] += val, add[rt] += val;
return;
}
int mid = gmid;
pushDown(rt);
if(L <= mid) update(l, mid, lson, L, R, val);
if(R > mid) update(mid + 1, r, rson, L, R, val);
pushUp(rt);
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
int t, cas = 0; scanf("%d", &t);
while(t--){
scanf("%d", &n);
mp.clear(); p1 = p2 = 0;
smx[p1] = {inf, 0}, smn[p2] = {0, 0};
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
build(1, n, 1); ll ret = 0;
for(int i = 1; i <= n; ++i){
int las = mp[a[i]];
update(1, n, 1, las + 1, i, -1);
mp[a[i]] = i;
while(a[i] >= smx[p1].first){
pii c = smx[p1--], b = smx[p1];
if(a[i] > c.first) update(1, n, 1, b.second + 1, c.second, a[i] - c.first);
}
smx[++p1] = {a[i], i};
while(a[i] <= smn[p2].first){
pii c = smn[p2--], b = smn[p2];
if(a[i] < c.first) update(1, n, 1, b.second + 1, c.second, c.first - a[i]);
}
smn[++p2] = {a[i], i};
ret += num[1];
}
printf("Case #%d: %lld\n", ++cas, ret);
}
}