题意:
给定长度为n的序列a。
问有多少个连续子序列,满足排序之后相邻元素的差值小于等于1。
数据范围:n<=1e5,1<=a(i)<=1e9
解法:
考虑枚举右端点r,统计有多少个满足条件的左端点.
对于区间[l,r],设区间最大值为ma,最小值为mi,数字种类cnt,
区间合法当且仅当ma-mi+1=cnt,即ma-mi-cnt=-1.
右移ma-mi+1总是>=cnt,
那么只需要维护每个l的ma-mi-cnt的最小值,
以及有多少个左端点l达到了最小值.
当r右移时,对于每个左端点l,ma,mi,cnt都会变化,
ma和mi用单调栈维护,cnt用last[]维护.
单调栈是找每个数作为max,min所能包含的区间范围.
last[]是找每个数对cnt有贡献的区间.
ma-mi-cnt用线段树维护,线段树同时维护一下最小值的数量.
因为对于r位置必定有ma-mi-cnt==-1,
即最小值一定是-1,因此直接ans+=T.cnt[1]即可.
参考:
https://blog.csdn.net/weixin_43823767/article/details/100188688
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=5e5+5;
int a[maxm];
int n;
struct Tree{
ll mi[maxm<<2];
ll cnt[maxm<<2];
ll laz[maxm<<2];
void pp(int node){
mi[node]=min(mi[node*2],mi[node*2+1]);
cnt[node]=0;
if(mi[node]==mi[node*2])cnt[node]+=cnt[node*2];
if(mi[node]==mi[node*2+1])cnt[node]+=cnt[node*2+1];
}
void pd(int node){
if(laz[node]){
laz[node*2]+=laz[node];
laz[node*2+1]+=laz[node];
mi[node*2]+=laz[node];
mi[node*2+1]+=laz[node];
laz[node]=0;
}
}
void build(int l,int r,int node){
laz[node]=0;
if(l==r){
cnt[node]=1;
mi[node]=0;
return ;
}
int mid=(l+r)/2;
build(l,mid,node*2);
build(mid+1,r,node*2+1);
pp(node);
}
void update(int st,int ed,int val,int l,int r,int node){
if(st<=l&&ed>=r){
laz[node]+=val;
mi[node]+=val;
return ;
}
pd(node);
int mid=(l+r)/2;
if(st<=mid)update(st,ed,val,l,mid,node*2);
if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
pp(node);
}
}T;
signed main(){
int TT;scanf("%d",&TT);
int cas=1;
while(TT--){
scanf("%d",&n);
T.build(1,n,1);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
ll ans=0;
stack<int>mi,ma;
map<int,int>last;
for(int i=1;i<=n;i++){
//维护ma:
while(!ma.empty()&&a[ma.top()]<a[i]){//单调递减栈
int x=ma.top();ma.pop();
if(ma.empty()){
T.update(1,x,-a[x],1,n,1);
}else{
T.update(ma.top()+1,x,-a[x],1,n,1);
}
}
if(ma.empty()){
T.update(1,i,a[i],1,n,1);
}else{
T.update(ma.top()+1,i,a[i],1,n,1);
}
ma.push(i);
//维护mi:
while(!mi.empty()&&a[mi.top()]>a[i]){//单调递增栈
int x=mi.top();mi.pop();
if(mi.empty()){
T.update(1,x,a[x],1,n,1);
}else{
T.update(mi.top()+1,x,a[x],1,n,1);
}
}
if(mi.empty()){
T.update(1,i,-a[i],1,n,1);
}else{
T.update(mi.top()+1,i,-a[i],1,n,1);
}
mi.push(i);
//维护cnt:
if(last[a[i]]){
T.update(last[a[i]]+1,i,-1,1,n,1);
}else{
T.update(1,i,-1,1,n,1);
}
last[a[i]]=i;
//累加ans:
ans+=T.cnt[1];
}
printf("Case #%d: %lld\n",cas++,ans);
}
return 0;
}