题目:
2019银川网络赛L:Continuous Intervals
题意:
定义一个连续区间:如果将 排序后,相邻元素的差的绝对值不大于 ,则称其为一个连续区间,现在给定包含 个元素的数组,求有多少个连续区间
分析:
虽然网络选拔赛直接拿原题有点恶心,但题还是好题
如果一个区间 符合连续区间的定义,设这个区间内的最大值为 ,最小值为 ,不同元素的个数为 ,那么会有:,这类统计区间个数的问题,需要套路的枚举一个端点,然后统计有多少个另一个端点符合条件,枚举左端点相当于删除数,枚举右端点相当于添加数;转化一下公式:,而对于任意区间有如下推论:,这意味着我们只需要维护区间中 的最小值及其个数,就可以快速得到另一个端点的数量;添加数容易维护区间最值,所以考虑枚举右端点 ,每次添加一个数(移动右端点),我们需要知道这个数作为最大值能影响的最远左端点 ,这个可以通过单调栈得到,那么我们就需要修改 的最大值;如果直接暴力修改,复杂度太高,考虑在 的值上再加上即将修改的最大值和原有最大值的差值就行了,所以还得记录每个最大值影响的区间,最小值同理,只需要用 记录每个数上次出现的位置就可维护
代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define PII pair<int,pii>
#define sz(x) (int)(x).size()
using namespace std;
typedef long long LL;
const int maxn = 1e5+16;
int T,n,m,a[maxn];
map<int,int> mp;
stack<PII> s1,s2; //PII.fi代表数值,PII.se代表这个数值影响的区间
LL lazy[maxn<<2],MIN[maxn<<2],cnt[maxn<<2];
inline void pushdown(int x){
MIN[x<<1] += lazy[x]; MIN[x<<1|1] += lazy[x];
lazy[x<<1] += lazy[x]; lazy[x<<1|1] += lazy[x];
lazy[x] = 0;
}
void BuildTree(int l,int r,int x){
cnt[x] = MIN[x] = lazy[x] = 0;
if(l == r){
cnt[x] = 1;
return ;
}
int mid = (l+r) >> 1;
BuildTree(l,mid,x<<1);
BuildTree(mid+1,r,x<<1|1);
}
void UpdataTree1(int l,int r,int L,int R,int x,LL val){
if(l > R || r < L) return;
if(l >= L && r <= R){
MIN[x] += val;
lazy[x] += val;
return ;
}
if(lazy[x]) pushdown(x);
int mid = (l+r)>>1;
UpdataTree1(l,mid,L,R,x<<1,val);
UpdataTree1(mid+1,r,L,R,x<<1|1,val);
MIN[x] = min(MIN[x<<1],MIN[x<<1|1]); cnt[x] = 0;
if(MIN[x] == MIN[x<<1]) cnt[x] += cnt[x<<1];
if(MIN[x] == MIN[x<<1|1]) cnt[x] += cnt[x<<1|1];
}
void QueryTree(int l,int r,int L,int R,int x,LL &res){
if(l > R || r < L) return ;
if(l >= L && r <= R){
if(MIN[x] == -1) res += cnt[x];
return ;
}
if(lazy[x]) pushdown(x);
int mid = (l+r) >> 1;
QueryTree(l,mid,L,R,x<<1,res);
QueryTree(mid+1,r,L,R,x<<1|1,res);
}
LL solve(){
mp.clear(); LL res = 0;
while(!s1.empty()) s1.pop();
while(!s2.empty()) s2.pop();
s1.push(PII(2e9,pii(0,0))); s2.push(PII(0,pii(0,0)));
for(int i = 1;i <= n; ++i){
UpdataTree1(1,n,i,i,1,-1);
if(!mp.count(a[i])) UpdataTree1(1,n,1,i-1,1,-1);
else UpdataTree1(1,n,mp[a[i]]+1,i-1,1,-1);
int last = i;
while(!s1.empty()&&s1.top().fi<a[i]){
UpdataTree1(1,n,s1.top().se.fi,last-1,1,a[i]-s1.top().fi);
last = s1.top().se.fi; s1.pop();
}
last = i;
while(!s2.empty()&&s2.top().fi>a[i]){
UpdataTree1(1,n,s2.top().se.fi,last-1,1,s2.top().fi-a[i]);
last = s2.top().se.fi; s2.pop();
}
mp[a[i]] = i; s1.push(PII(a[i],pii(s1.top().se.se+1,i)));s2.push(PII(a[i],pii(s2.top().se.se+1,i)));
QueryTree(1,n,1,i,1,res);
}
return res;
}
int main(){
int T; cin >> T;
for(int Case = 1;Case <= T; ++Case){
scanf("%d",&n); BuildTree(1,n,1);
for(int i = 1;i <= n; ++i) scanf("%d",a+i);
printf("Case #%d: %lld\n",Case,solve());
}
return 0;
}