题意:给一个序列,问长度为L~R的子串和中前k大的和
对于O(n^2)的区间枚举,考虑枚举一个点,计算另一个点
于是枚举左端点,计算出在范围内且使区间和最大的右端点(ST表完成)
这时候的最大值一定是全局最大值,取出后考虑以该左端点为左端点的区间次大值,设原区间在右端点y处取到最大,则将y删去后的两个区间有可能取到次大值,加入堆即可
复杂度O(nlogn)
ST表的边界一定要注意!log2能手写就手写咯
#include<iostream> #include<cstdio> #include<queue> #include<cmath> using namespace std; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } const int MAXN = 1000005; int n,m,L,R; int sum[MAXN],f[2*MAXN][33],g[2*MAXN][33]; int lg2(int x){ int ret=1; while((1<<ret)<x)ret++; return ret-1; } int query(int l,int r){ if(l==r)return g[l][0]; int len=lg2(r-l+1); return f[l][len]<f[r-(1<<len)+1][len]?g[r-(1<<len)+1][len]:g[l][len]; } struct Node{ int x,y,l,r; Node(int _x=0,int _l=0,int _r=0){ x=_x;l=_l;r=_r; y=query(l,r); // cout<<"Push:"<<x<<" "<<y<<" "<<l<<" "<<r<<endl; } bool operator <(const Node &rhs)const{ return sum[y]-sum[x-1]<sum[rhs.y]-sum[rhs.x-1]; } }tmp; priority_queue<Node> Q; int main(){ n=rd();m=rd();L=rd();R=rd(); for(int i=1;i<=n;i++){ sum[i]=sum[i-1]+rd(); f[i][0]=sum[i];g[i][0]=i; } for(int j=1;(1<<j)<=n;j++){ for(int i=1;i<=n;i++){ if(f[i][j-1]>f[i+(1<<(j-1))][j-1]){ f[i][j]=f[i][j-1]; g[i][j]=g[i][j-1]; }else{ f[i][j]=f[i+(1<<(j-1))][j-1]; g[i][j]=g[i+(1<<(j-1))][j-1]; } } } for(int i=1;i+L-1<=n;i++){ int l=i+L-1,r=i+R-1; r=min(r,n); Q.push(Node(i,l,r)); } long long ans=0; int cnt=0; int x,y,u,v; while(cnt<m){ tmp=Q.top();Q.pop(); x=tmp.x,y=tmp.y,u=tmp.l,v=tmp.r; // cout<<x<<" "<<y<<endl; ans+=sum[y]-sum[x-1]; if(y-1>=u){Q.push(Node(x,u,y-1));} if(y+1<=v){Q.push(Node(x,y+1,v));} cnt++; } cout<<ans; return 0; }