f(l,r,K)表示区间l,r里面的K大值,问你所有连续子区间的f之和。
l(i)表示i左侧第一个比它大的数的位置,r(i)表示i右侧第一个比它大的数的位置。可以用set处理出来。
把数从大到小排序,依次插入。然后更新l(i),r(i),形成链形结构。
然后对于一个i,向左跳最多K次,将这些位置记录下来,然后向右跳最多K次,每个右侧的位置最多有一个左侧的位置合法。累计答案。
#include<cstdio> #include<set> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; set<int>S; typedef set<int>::iterator ITER; ll ans; int T,n,K,a[500010],b[500010],L[500010],R[500010]; int ls[500010],rs[500010]; bool cmp(const int &i,const int &j){ return a[i]>a[j]; } int main(){ // freopen("1003.in","r",stdin); // freopen("1003.out","w",stdout); scanf("%d",&T); for(;T;--T){ S.clear(); ans=0; scanf("%d%d",&n,&K); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); } for(int i=1;i<=n;++i){ b[i]=i; } sort(b+1,b+n+1,cmp); for(int i=1;i<=n;++i){ ITER it=S.lower_bound(b[i]); if(i>1 && it!=S.begin()){ --it; L[b[i]]=*it; R[*it]=b[i]; } else{ L[b[i]]=-1; } it=S.upper_bound(b[i]); if(i>1 && it!=S.end()){ R[b[i]]=*it; L[*it]=b[i]; } else{ R[b[i]]=-1; } int e1=0,e2=0; ls[0]=rs[0]=b[i]; for(int j=b[i],l=1;l<=K && L[j]!=-1;j=L[j],++l){ ls[++e1]=L[j]; } if(e1<K){ ls[++e1]=0; } for(int l=1,j=b[i];l<=K && R[j]!=-1;j=R[j],++l){ rs[++e2]=R[j]; if(e2-1+(ls[e1]==0 ? e1-1 : e1)>=K-1){ ans+=(ll)(ls[K-e2]-ls[K-e2+1])*(ll)(rs[e2]-rs[e2-1])*(ll)a[b[i]]; } } if(e2<K){ rs[++e2]=n+1; } if(rs[e2]==n+1 && e2-1+((e1==K || ls[e1]==0) ? e1-1 : e1)>=K-1){ ans+=(ll)(ls[K-e2]-ls[K-e2+1])*(ll)(rs[e2]-rs[e2-1])*(ll)a[b[i]]; } else if(K==1){ ans+=(ll)(b[i]-(L[b[i]]==-1 ? 0 : L[b[i]]))*(ll)((R[b[i]]==-1 ? n+1 : R[b[i]])-b[i])*(ll)a[b[i]]; } S.insert(b[i]); } cout<<ans<<endl; } return 0; }