http://acm.hdu.edu.cn/showproblem.php?pid=6701
题意:给一个数组,求有多少个[l,r],使得[l,r]内所有元素不重复,且
思路:
不断考虑中间的最大值,枚举左端点或者右端点(需要枚举的数量少的那边),然后确定另一个端点的范围。
预处理出一个ST表,维护区间最大值。
预处理出向左向右能够延申到的最远的地方(虾膏
通过递归分治,每次枚举数量少的那一边。因为每一个元素最多固定成一个端点(然后计算另一个端点的范围)最多log次,所以复杂度为nlogn。
/* (o O o)
* Author : Rshs
* Data : 2019-08-28-20.40
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 3e5+5;
const int logMX = 21+2;
int st[MX][logMX],a[MX];//维护的最大值
void build(int n){ //记录的下标,如果需要记录真实值,去掉a[]
for(int i=1;i<=n;i++) st[i][0]=i;
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){ //全部维护的区间都是合法的即l>=0,r<=n
if(a[st[i][j-1]]>a[st[i+(1<<(j-1))][j-1]]) st[i][j]=st[i][j-1];
else st[i][j]=st[i+(1<<(j-1))][j-1];
}
}
}
int query(int l,int r){ //l到大于mid的位置,小于mid的位置到r
int lloo=(int)(log2((double)(r-l+1)));
if(a[st[l][lloo]]>a[st[r-(1<<lloo)+1][lloo]]) return st[l][lloo];
return st[r-(1<<lloo)+1][lloo];
}
int L[MX],R[MX],to[MX],cnt[MX];LL ans=0;int n,k;
void LANDR(int n){
for(int i=1;i<=n;i++) to[i]=1,cnt[i]=0;
int pos=1;
for(int i=1;i<=n;i++){
if(cnt[a[i]])pos=max(pos,to[a[i]]);
to[a[i]]=i+1;cnt[a[i]]++;L[i]=pos;
}
for(int i=1;i<=n;i++) to[i]=n,cnt[i]=0;
pos=n;
for(int i=n;i>=1;i--){
if(cnt[a[i]])pos=min(pos,to[a[i]]);
to[a[i]]=i-1;cnt[a[i]]++;R[i]=pos;
}
}
void dfs(int l,int r){
if(l>r) return;
int pmx=query(l,r);
int le=a[pmx]-k;
if(r-pmx>pmx-l){
for(int i=l;i<=pmx;i++){//左端点固定确定右端点范围
int ll=max(pmx,i+le-1);
int rr=min(R[i],r);
ans=ans+(LL)max(rr-ll+1,0);
}
}
else{
for(int i=pmx;i<=r;i++){
int rr=min(pmx,i-le+1);
int ll=max(L[i],l);
ans=ans+(LL)max(rr-ll+1,0);
}
}
dfs(l,pmx-1),dfs(pmx+1,r);
}
void Main(){
cin>>n>>k;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build(n);
LANDR(n);
ans=0;
dfs(1,n);
cout<<ans<<'\n';
}
int main(){
int ca;cin>>ca;while(ca--)Main();
return 0;
}