题目链接、
题意:
求有多少对,使得内无重复数字,且
题解:
预处理的管辖区间
处理一颗笛卡尔树,遍历整棵树,启发式分治区间
一、
对于,我们先处理一个上次出现的位置+1,然后取一个前缀
对于,我们先处理一个上次出现的位置-1,然后取一个后缀
这想法个来自这里、
二、直接暴力跑区间
1、设一个r,不断往右走,一边走一边标记,
当遇到标记时,记录当前位置-1,即应该在的位置
并清空标记
2、设一个l,不断往左走,一边走一边标记,
当遇到标记时,记录当前位置+1,即应该在的位置
并清空标记
这个想法来自这里、
int r = 2;
vis[a[1]] = 1;
for(int i = 1; i <= n; ++i) {
while(r <= n && !vis[a[r]]) {
vis[a[r]] = 1;
++r;
}
vis[a[i]] = 0;
R[i] = r - 1;
}
vis[a[n]] = 1;
int l = n - 1;
for(int i = n; i >= 1; --i) {
while(l >= 1 && !vis[a[l]]) {
vis[a[l]] = 1;
--l;
}
vis[a[i]] = 0;
L[i] = l + 1;
}
考虑求答案,即整个的贡献的时候,注意到我们需要的是
当前整个区间最大值的所在位置,然后递归左右区间,
这和笛卡尔树的定义一样,
笛卡尔树根root,表示的是整个区间最大值的位置所在下标
左儿子则是左区间的最大值位置
右儿子则是右区间的最大值位置
笛卡尔树
单调栈建树时间复杂度,感觉比求一个st表的常数小得多
(实际上也的确如此,我这份随意写的菜鸡代码溜到了提交第一页的最后一名)
/*author:revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+7;
int cnt,n,k;
int l[maxn],r[maxn],stl[maxn],top,root;//tree
int val[maxn],vis[maxn],Lc[maxn],Rc[maxn];
ll ans;
void dfs(int L,int R,int mid){
if(L>R)return;
int len = val[mid]-k;
if(mid-L<R-mid){
for(int i=mid;i>=L;i--){//枚举L
int tl = max(mid,i+len-1);//计算r、即r >= val[mid] - k + L -1
int tr = min(Rc[i],R);//合法区间
if(tl <= tr)ans += tr-tl+1;//有多少个在合法区间内
}
}else{
for(int i=mid;i<=R;i++){//同理
int tl = max(L,Lc[i]);
int tr = min(i-len+1,mid);
if(tl <= tr)ans += tr-tl+1;
}
}
dfs(L,mid-1,l[mid]);
dfs(mid+1,R,r[mid]);
}
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&val[i]);
//insert i
while(top && val[stl[top]] < val[i])l[i] = stl[top--];
if(top)r[stl[top]] = i;
stl[++top] = i;
}
while(top)root = stl[top--];
for(int i=1;i<=n;i++)vis[i] = 0;
for(int i=1;i<=n;i++)Lc[i] = vis[val[i]]+1,vis[val[i]] = i;
for(int i=2;i<=n;i++)Lc[i] = max(Lc[i],Lc[i-1]);
for(int i=n;i>=1;i--)vis[i] = n+1;
for(int i=n;i>=1;i--)Rc[i] = vis[val[i]]-1,vis[val[i]] = i;
for(int i=n;i>=2;i--)Rc[i-1] = min(Rc[i-1],Rc[i]);
ans = 0;
dfs(1,n,root);
printf("%d\n",ans);
}
return 0;
}