题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6701
题意:
你有一个长为nnn的数组。现在定义一段连续的子区间 [l,r][l,r][l,r] 为“好的”,当且仅当max(al,al+1,…,ar)−(r−l+1)≤kmax(a_l,a_{l+1},…,a_r)−(r−l+1)≤kmax(al,al+1,…,ar)−(r−l+1)≤k ,并且这个区间中没有重复的值,问你能找到多少个"好的"子区间。
做法:
有种经典套路的感觉 (做题不多的我最近好像碰到蛮多这样的题)。我们枚举自己作为最大值可以影响的区间,即对每一个值我们都求出它作为最大值可以影响到的最远的左端和右端。这个操作可以用笛卡尔树来解决(即一个手写的O(n)O(n)O(n)的最大(小)堆),如果不知道笛卡尔树是什么的…找大佬的博客叭。
然后我们就要开始分治了,对于每个最大值,我们找这个合法区间中左边和右边元素比较少的一侧进行枚举,注意是元素比较少的一侧,这个优化可以把最坏情况下O(n2)O(n^2)O(n2)的复杂度优化到O(nlog2n)O(nlog_2n)O(nlog2n)。 想象一下,不优化的话,最坏情况就是最值每次都在角落,然后枚举的位置总是多的一侧,这样就是O(n2)O(n^2)O(n2)的,但是如果枚举少的,那么每次最值都在中间的时候才会出现最坏情况O(nlog2n)O(nlog_2n)O(nlog2n)。 然后我们枚举少的那一侧,复杂度就是O(n(log2n)2)O(n(log_2n)^2)O(n(log2n)2)。在这道题中是可以接受的。
我们在进行分治之前,还需要做一个处理,即对每一个位置,找出以它为左端无重复元素的右端,和以它为右端无重复元素的左端。这个东西可以保证我们在枚举的时候对于每个元素只需要O(1)O(1)O(1)的时间就能算出答案,这个左右端用尺取就能做到。在这里还要对情况进行讨论,如果这个区间超过了我们分治的区间,那么要压缩,还有其他的细节就看代码叭。
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn=300005;
int a[maxn],sta[maxn],rson[maxn];
int vis[maxn],lson[maxn],top,n,k;
int L[maxn],R[maxn],num[maxn];
ll ans;
void dfs(int l,int r,int rt){
if(r<l)return ;
int nma=a[rt],minl=a[rt]-k;
if(r-rt>rt-l){//右边比左边大 for左边
for(int i=l;i<=rt;i++){
if(L[i]<rt||min(L[i],r)-i+1<minl) continue;
ans+=(ll)(min(L[i],r)-max(i+minl-1,rt)+1);
}
}
else{//左边比右边大 for右边
for(int i=r;i>=rt;i--){
if(R[i]>rt||i-max(R[i],l)+1<minl) continue;
ans+=(ll)(min(i-minl+1,rt)-max(R[i],l)+1);
}
}
dfs(l,rt-1,lson[rt]);
dfs(rt+1,r,rson[rt]);
}
int main(){
/*freopen("data.in","r",stdin);
freopen("data.out","w",stdout);*/
int T; scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&k);
top=0;
rep(i,1,n){
scanf("%d",&a[i]);
vis[i]=false;
rson[i]=lson[i]=0;
L[i]=R[i]=num[i]=0;
}
int r=0,l=n+1;
rep(i,1,n){
while(r<n&&num[a[r+1]]==0){
r++; num[a[r]]++;
}
L[i]=r;
num[a[i]]--;
}
for(int i=n;i>=1;i--){
while(l>1&&num[a[l-1]]==0){
l--; num[a[l]]++;
}
R[i]=l;
num[a[i]]--;
}
rep(i,1,n){
int tmp=top,flag=0;
while(tmp&&a[sta[tmp]]<=a[i]) {
flag=sta[tmp];
tmp--;
}
if(tmp) rson[sta[tmp]]=i;
if(top>tmp) lson[i]=flag;
sta[++tmp]=i;
top=tmp;
}
for(int i=1;i<=n;i++) vis[lson[i]]=vis[rson[i]]=true;
int rt=1;
while(vis[rt]==true) rt++;
ans=0;
dfs(1,n,rt);
printf("%lld\n",ans);
}
return 0;
}