[BZOJ] 2006: [NOI2010]超级钢琴

题意:给一个序列,问长度为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;
}

 

posted @ 2018-09-18 17:23  GhostCai  阅读(153)  评论(1编辑  收藏  举报