题目地址
题目大意:定义一个美丽区间是指将一个区间排序之后任意相邻两个数的差的绝对值不超过1,然后给你
n
n
n个数,问你美丽区间的数量。
Solution:
- 对于这种区间计数类问题,我们有一个比较明确的方向就是固定一个端点然后查看另一个端点的合法情况有多少个。
- 我们发现如果一个区间合法,那么这个区间一定满足 m a x n − m i n n + 1 − c n t = 0 maxn-minn+1-cnt=0 maxn−minn+1−cnt=0,其中 m a x n maxn maxn代表区间最大值, m i n n minn minn代表区间最小值, c n t cnt cnt代表区间不同数的个数。通过观察我们可以发现,假如一个区间是不合法的那么这个区间的 m a x n − m i n n + 1 − c n t maxn-minn+1-cnt maxn−minn+1−cnt的值一定是大于 0 0 0的,因为我们可以假设区间最值没有发生变化,那么让区间不合法的原因就是区间不同数的数量比较少,那么我们最后的值一定是大于0的,综上我们可以得到一个式子 m a x n − m i n n − c n ≥ − 1 maxn-minn-cn\geq-1 maxn−minn−cn≥−1。
- 我们考虑用线段树维护上面的式子,因为线段树可以比较容易的解决区间最值计数问题。
- 现在我们考虑让我们枚举右端点的时候如何去更新答案,当我们的右端点移动到R时,我们要考虑某些区间的最值会如何变化,这里可以用单调栈去很轻松的维护出来,当某个数出栈的时候我们消除它的贡献,当一个数进栈的时候我们去加上它的贡献。
- 关于计算区间不同数的数量这个问题,我们可以用 m a p map map去记录这个数上次出现的位置,同样也可以轻松的维护。
如果不是很懂的话没关系,可以直接的看下面的代码,中间给了很清楚的注释。
#include<bits/stdc++.h>
#define endl "\n"
#define x first
#define y second
#define Endl endl
typedef long long ll;
const int INF=0x7fffffff;
const double PI=3.14159265359;
using namespace std;
const int N=1e5+10;
map<int,int>col;
int a[N];
int idx=0;
int stk1[N],stk2[N],idx1,idx2;//stk1维护max stk2维护min
struct point{//线段树维护区间 maxn-minn+1-cnt的最小值以及最小值的数量
int l,r;
int minn;
int sum;
int lazy;
}tr[N*4];
void pushdown(int root){
if(tr[root].lazy!=0){
tr[root].minn+=tr[root].lazy;
if(tr[root].l!=tr[root].r){
int ch=root<<1;
tr[ch].lazy+=tr[root].lazy;
tr[ch+1].lazy+=tr[root].lazy;
}
tr[root].lazy=0;
}
}
void pushup(int root){
int ch=root<<1;
pushdown(ch),pushdown(ch+1);
tr[root].minn=min(tr[ch].minn,tr[ch+1].minn);
tr[root].sum=0;
if(tr[root].minn==tr[ch].minn) tr[root].sum+=tr[ch].sum;
if(tr[root].minn==tr[ch+1].minn) tr[root].sum+=tr[ch+1].sum;
}
void build(int root,int l,int r){
tr[root].l=l,tr[root].r=r;
tr[root].sum=1;
tr[root].minn=0;
tr[root].lazy=0;
if(l!=r){
int ch=root<<1;
int mid=(l+r)>>1;
build(ch,l,mid);
build(ch+1,mid+1,r);
}
}
void change(int root,int l,int r,int x){
pushdown(root);
if(tr[root].l==l&&tr[root].r==r){
tr[root].lazy+=x;
return;
}
int mid=(tr[root].l+tr[root].r)>>1;
int ch=root<<1;
if(r<=mid) change(ch,l,r,x);
else if(l>mid) change(ch+1,l,r,x);
else change(ch,l,mid,x),change(ch+1,mid+1,r,x);
pushup(root);
}
void solve(){
ll ans=0;
idx1=0,idx2=0;
col.clear();
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<"Case #"<<++idx<<": ";
build(1,1,n);
for(int i=1;i<=n;i++){//枚举右端点
int l,r,temp;
//考虑维护最大值的情况
while(idx1&&a[i]>a[stk1[idx1]]){//我要消除之前的影响
temp=a[stk1[idx1]];//a[stk1[idx1]]作为最大值时的区间范围是[l,r]
r=stk1[idx1];
idx1--;
l=stk1[idx1]+1;
change(1,l,r,-temp);
}
l=stk1[idx1]+1;
stk1[++idx1]=i;
r=i;
change(1,l,r,a[i]);//加上新加入的那个数的影响
//考虑维护最小值的情况
while(idx2&&a[i]<a[stk2[idx2]]){
temp=a[stk2[idx2]];
r=stk2[idx2];
idx2--;
l=stk2[idx2]+1;
change(1,l,r,temp);
}
l=stk2[idx2]+1;
stk2[++idx2]=i;
r=i;
change(1,l,r,-a[i]);
//考虑维护cnt
if(col[a[i]]) change(1,col[a[i]]+1,i,-1);
else change(1,1,i,-1);
col[a[i]]=i;
ans+=tr[1].sum;
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--){
solve();
}
}