题目:
2019杭电暑假多校10:Make Rounddog Happy
题意:
给定一个长度为N的数组,问有多少个子数组Array[L....R]满足max{Array[L....R]} - (R-L+1) <= K
分析:
这种求子段数目的题,一般都要套路的枚举一下一个端点,假设枚举左端点L,首先要知道最大R能取到多少,也就是说Array[L....R]内没有重复出现的数字,且R最大;这个可以用dp的思想从后往前O(N)预处理得到;然后R还得满足K+R-L+1-max{Array[L....R]} >= 0;只有R和前缀最大值未知;这个可以在线段树上处理出区间最大值,那怎么查询R的数量呢?线段树在递归的过程中先走左子树,再走右子树,走左子树的时候顺便取区间最大值,这不就得到前缀最大值了吗;设从L开始的前缀最大值为maxv,假设走到了线段树[xl,xr]这个区间,如果K+xl-L+1-maxv >= 0 ,那么[xl,xr]肯定都可以,如果K+xr-L+1-maxv < 0,那么[xl,xr]肯定都不可以,结束递归,否则继续递归下去
代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 3e5+35;
int T,n,k,a[maxn],pos[maxn],vis[maxn],MAX[maxn<<2];
inline int read() {
int x = 0, f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
return x * f;
}
inline void write(LL x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
void BuildTree(int l,int r,int x){
if(l == r){
MAX[x] = a[r];
return ;
}
int mid = (l+r)>>1;
BuildTree(l,mid,x<<1);
BuildTree(mid+1,r,x<<1|1);
MAX[x] = max(MAX[x<<1],MAX[x<<1|1]);
}
LL ans; int maxv;
void QueryTree(int l,int r,int L,int R,int x,int v,int &maxv){
if(l > R || r < L) return ;
if(l >= L && r <= R){
if(k+l+1-v-max(maxv,MAX[x])>=0){
ans += r-l+1;
maxv = max(maxv,MAX[x]);
return ;
}
if(l == r || k+r+1-v-maxv<0) {
maxv = max(maxv,MAX[x]);
return ;
}
}
int mid = (l+r) >> 1;
QueryTree(l,mid,L,R,x<<1,v,maxv);
QueryTree(mid+1,r,L,R,x<<1|1,v,maxv);
}
LL solve(){
ans = 0;
for(int i = 1;i <= n; ++i){
if(a[i]-1<=k) ans++;
maxv = a[i];
if(i+1<pos[i]) QueryTree(1,n,i+1,pos[i]-1,1,i,maxv);
}
return ans;
}
int main(){
T = read();
while(T--){
n = read(), k = read();
for(int i = 1;i <= n; ++i) a[i] = read(),vis[i] = n+1;
pos[n] = n+1; vis[a[n]] = n;
for(int i = n-1;i > 0; --i){ //预处理L=i时,R的最大值
pos[i] = min(vis[a[i]],pos[i+1]);
vis[a[i]] = i;
}
BuildTree(1,n,1);
write(solve()),putchar('\n');
}
return 0;
}