题意:给定数组a[],求区间个数,满足区间的数各不同,而且满足maxval-len<=K;
思路:一看就可以分治做,对于当前的区间,从max位置分治。 对于这一层,需要高效的统计答案,那么对短的一边开始统计。
(这个过程很像启发式的逆过程,所以叫做启发式分治
1,对于数不同,这个可以预处理前缀和后缀的最大区间长度A[],B[]。
2,st表得到区间最大值位置,然后就可以搞了。
如果是第一次遇到,可以参考同一类的题目:
2019牛客暑期多校训练营(第三场)G: Removing Stones(启发式分治)
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; int a[maxn],st[maxn][21],lg[maxn],K,N; ll ans; int A[maxn],B[maxn],vis[maxn]; int get(int L,int R) { int k=lg[R-L+1]; return a[st[L][k]]>=a[st[R-(1<<k)+1][k]]?st[L][k]:st[R-(1<<k)+1][k]; } void solve(int L,int R) { if(L>R) return ; int pos=get(L,R); if(pos-L<R-pos){ rep(i,L,pos){ int t=a[pos]-K,lR=i+t-1; int fcy=min(R,B[i]); lR=max(lR,pos); if(lR>fcy) continue; ans+=fcy-lR+1; } } else { rep(i,pos,R){ int t=a[pos]-K,rL=i-t+1; int fcy=max(L,A[i]); rL=min(rL,pos); if(rL<fcy) continue; ans+=rL-fcy+1; } } solve(L,pos-1); solve(pos+1,R); } int main() { int T; lg[0]=-1; rep(i,1,maxn-1) lg[i]=lg[i>>1]+1; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&K); ans=0; rep(i,1,N) scanf("%d",&a[i]); rep(i,1,N) st[i][0]=i; rep(i,1,20) { rep(j,1,N+1-(1<<i)) st[j][i]=a[st[j][i-1]]>=a[st[j+(1<<(i-1))][i-1]]?st[j][i-1]:st[j+(1<<(i-1))][i-1]; } rep(i,1,N) vis[i]=0; A[1]=1; vis[a[1]]=1; rep(i,2,N){ if(vis[a[i]]) A[i]=max(A[i-1],vis[a[i]]+1); else A[i]=A[i-1]; vis[a[i]]=i; } rep(i,1,N) vis[i]=0; B[N]=N; vis[a[N]]=N; for(int i=N-1;i>=1;i--){ if(vis[a[i]]) B[i]=min(B[i+1],vis[a[i]]-1); else B[i]=B[i+1]; vis[a[i]]=i; } solve(1,N); printf("%lld ",ans); } return 0; }