DP維護從某點 往後/往前 遇到的第一對非法點對。
然後分治計數,合併的時候根據最大值是 由左區間貢獻還是右區間貢獻 來進行分類,注意去重。
可以枚舉左/右區間端點,這樣即能固定最大值也能固定剩餘區間的長度。
複雜度O(NlogN)
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define pii pair<long long,long long>
#define ll long long
#define pb push_back
#define all(x) (x).begin,(x).end()
const int N=3e5+100;
using namespace std;
int s[N],n,k,dp1[N],nxt[N],pre[N],dp2[N];
ll f(int l,int r) {
if(l==r) {
if(s[l]-1<=k) return 1;
else return 0;
}
int mid = (l+r)>>1;
ll ans = f(l,mid)+f(mid+1,r);
int now = 0;
int p = mid+1;
per(i, mid, l) {
now = max(now,s[i]);
int R = min(dp1[i]-1,r);
while(s[p]<=now&&p<=r) p++;
R = min(R,p-1);
int L = mid + now-k-(mid-i+1);
L = max(L,mid+1);
if(L<=R) ans += (R-L+1);
}
now = 0;
p = mid;
rep(i, mid+1, r){
now = max(now,s[i]);
int L = max(dp2[i]+1,l);
while(s[p]<now&&p>=l) p--;
L = max(L,p+1);
int R = mid - (now-k-(i-mid)) +1;
R = min(R,mid);
if(L<=R) ans += (R-L+1);
}
return ans;
}
int main() {
//freopen("a.txt","r",stdin);
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--) {
cin>>n>>k;
rep(i, 1, n) cin>>s[i];
rep(i, 1, 300000) nxt[i] = n+1,pre[i] = 0;
dp1[n+1] = n+1;
per(i, n, 1) {
dp1[i] = min(dp1[i+1],nxt[s[i]]);
nxt[s[i]] = i;
}
dp2[0] = 0;
rep(i, 1, n) {
dp2[i] = max(dp2[i-1],pre[s[i]]);
pre[s[i]] = i;
}
cout<<f(1,n)<<endl;
}
return 0;
}