题意:题目大意:给定一个长度为 N 的序列,定义连续区间 [l, r] 为:序列的一段子区间,满足 [l, r] 中的元素从小到大排序后,任意相邻两项的差值不超过1。求一共有多少个连续区间。
题解:某一个连续区间等价于max-min+1=cnt,
cnt为区间内不同的数的个数,我们变形一下可以得到max-min-cnt==-1,所以我们固定当前的区间右端点R,用线段树维护在(1,R)区间内有多少个区间满足max-min-cnt==-1即可。线段树内维护的是一个区间内的各个子区间内满足max-min-cnt==-1的个数。
注意这里我们维护max 和 min去不断更新区间的时候,我看很多博客直接就去说用单调栈维护就行了,这里我想了好长时间。之所以能用单调栈做是因为对于一个单调栈内相邻的两个元素w[a],w[b],(a,b表示下标),在[a+1,b]范围内w[b]一定是最值,所以对于每个单调栈中我们就可以在单调栈更新的过程中去更新相邻元素的这个区间段,这样的区间段一定是不重不漏的,这样扫描的时间复杂度就从N^2降到了N。第一次见单调栈这样优化,学到了。
AC代码:
#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
#define int long long
#define p first
#define k second
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+5;
int n;
int w[N];
PII smin[N],smax[N];
struct node{
int l,r;
int m,sum,add;//m表示区间内max-min-cnt的最小值
}tr[N*2];
void pushup(int u){
auto l=tr[u<<1],r=tr[u<<1|1];
tr[u].m=min(l.m,r.m);
if(l.m>r.m) tr[u].sum=r.sum;
if(l.m<r.m) tr[u].sum=l.sum;
if(l.m==r.m) tr[u].sum=l.sum+r.sum;
}
void pushdown(int u){
if(tr[u].add){
auto &l=tr[u<<1],&r=tr[u<<1|1];
auto add=tr[u].add;
l.add+=add,l.m+=add;
r.add+=add,r.m+=add;
tr[u].add=0;
}
}
void build(int u,int l,int r){
tr[u]={l,r,0,0,0};
if(l==r){
tr[u].sum=1;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void update(int u,int l,int r,int v){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].add+=v;
tr[u].m+=v;
return;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)update(u<<1,l,r,v);
if(r>mid)update(u<<1|1,l,r,v);
pushup(u);
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
if(tr[u].m==-1)return tr[u].sum;
return 0;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1,res=0;
if(l<=mid) res+=query(u<<1,l,r);
if(r>mid) res+=query(u<<1|1,l,r);
return res;
}
main(){
int T,Case=0;
cin>>T;
while(T--){
scanf("%lld",&n);
build(1,1,n);
map<int,int>mp;
int res=0,t1=0,t2=0;
smax[0]={0,0},smin[0]={0,0};
for(int i=1;i<=n;i++){
int k;
scanf("%lld",&k);
while(t1&&smax[t1].k<k){
int v=k-smax[t1].k;
int l=smax[t1-1].p+1,r=smax[t1].p;
t1--;
update(1,l,r,v);//更新max改变对max-min-cnt照成的影响
}
smax[++t1]={i,k};
while(t2&&smin[t2].k>k){
int v=smin[t2].k-k;
int l=smin[t2-1].p+1,r=smin[t2].p;
t2--;
update(1,l,r,v);//更新min改变对max-min-cnt照成的影响
}
smin[++t2]={i,k};
update(1,mp[k]+1,i,-1);//更新cnt改变对max-min-cnt照成的影响
mp[k]=i;
res+=query(1,1,i);
}
printf("Case #%lld: %lld\n",++Case,res);
}
}