【BZOJ 2006】[NOI2010]超级钢琴 ST

我们先把所有最左端对应的最优右端入堆,eg: z  在[l,r](由题目给出的L,R决定)之间的最优解 y,然后出堆以后,再入堆z,y-1,z,y+1,那么我们只需要用st找最大前缀和就好了(ST是一种用来解决RMQ问题的方法他的应用也就限于此了)

#include <cstdio>
#include <cstring>
#include <queue>
#define make(a,b,c,d) (DT){a,b,c,d}
#define MAXN 500000
using namespace std;
int bin[20],st[MAXN+10][20],Log[MAXN+10];
int n,k,L,R,s[MAXN+10],a[MAXN+10];
long long ans;
struct DT
{
  int z,l,r,y;
  bool operator < (const DT next)const  {  return s[next.y]-s[next.z-1]>s[y]-s[z-1];  }
};
inline int Min(int x,int y)
{
  return x<y?x:y;
}
inline void pre()
{
  bin[0]=1;for(register int i=1;i<20;i++)bin[i]=bin[i-1]<<1;
  Log[0]=-1;for(register int i=1;i<=n;i++)Log[i]=Log[i>>1]+1;
  for(register int i=1;i<=n;i++)st[i][0]=i;
  for(register int i=1;i<20;i++)
    for(register int j=1;j<=n;j++)
      if(j+bin[i]-1<=n) st[j][i]=s[st[j][i-1]]>s[st[j+bin[i-1]][i-1]]?st[j][i-1]:st[j+bin[i-1]][i-1];
}
priority_queue<DT> Q;
inline int query(int x,int y)
{
  register int len=Log[y-x+1];
  return s[st[x][len]]>s[st[y-bin[len]+1][len]]?st[x][len]:st[y-bin[len]+1][len];
}
inline void Init()
{
  scanf("%d%d%d%d",&n,&k,&L,&R);
  for(register int i=1;i<=n;i++)scanf("%d",&a[i]);
  for(register int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
  pre();
  for(register int i=1,r;i<=n;i++)
    if(i+L-1<=n) r=Min(n,i+R-1),Q.push(make(i,i+L-1,r,query(i+L-1,r)));
}
inline void Work()
{
  while(k--)
  {
    DT x=Q.top();Q.pop();
    ans+=s[x.y]-s[x.z-1];
    if(x.y-1>=x.l)Q.push(make(x.z,x.l,x.y-1,query(x.l,x.y-1)));
    if(x.y+1<=x.r)Q.push(make(x.z,x.y+1,x.r,query(x.y+1,x.r)));
  }
  printf("%lld",ans);
}
int main()
{
  Init();
  Work();
  return 0;
}

 

posted @ 2017-08-02 06:33  TS_Hugh  阅读(195)  评论(0编辑  收藏  举报