BZOJ 2006 [NOI2010]超级钢琴
题解:
枚举左端点,找到合法的最大的右端点
依次选取m段
当某一段的最大右端点被选过了,才能选次大右端点
开一个堆,维护元素(begin,maxp,l,r)表示当前begin为左端点,l,r为合法区间,maxp为最大右端点
每次选取贡献最大的区间,然后按maxp分裂成两部分,次大右端点在这两部分中,放入堆里
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<cmath> using namespace std; const int maxn=500009; typedef long long Lint; int n,m,lo,hi; Lint ans; int s[maxn]; struct Sec{ int b,p,l,r; bool operator < (const Sec &rhs) const{ return s[p]-s[b-1]<s[rhs.p]-s[rhs.b-1]; } Sec(int bb,int pp,int ll,int rr){ b=bb;p=pp;l=ll;r=rr; } }; priority_queue<Sec>q; int f[maxn][20]; int g[maxn][20]; void STinit(){ for(int i=1;i<=n;++i){ f[i][0]=s[i]; g[i][0]=i; } for(int j=1;j<=19;++j){ for(int i=1;i+(1<<j)-1<=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]; } } } } int Querymax(int l,int r){ int k=log2(r-l+1.5); if(f[l][k]>f[r-(1<<k)+1][k]){ return g[l][k]; }else{ return g[r-(1<<k)+1][k]; } } int main(){ scanf("%d%d%d%d",&n,&m,&lo,&hi); for(int i=1;i<=n;++i){ scanf("%d",&s[i]); s[i]+=s[i-1]; } STinit(); for(int i=1;i<=n;++i){ int tl=i+lo-1; int tr=min(i+hi-1,n); if(tl>tr)continue; int p=Querymax(tl,tr); q.push(Sec(i,p,tl,tr)); } while(m--){ Sec t=q.top();q.pop(); int b=t.b; int p=t.p; int tl=t.l; int tr=t.r; int p1; ans+=(s[p]-s[b-1]); if(p!=tl){ p1=Querymax(tl,p-1); q.push(Sec(b,p1,tl,p-1)); } if(p!=tr){ p1=Querymax(p+1,tr); q.push(Sec(b,p1,p+1,tr)); } } cout<<ans<<endl; return 0; }
致歉:笔者已经意识到这是一篇几乎没有价值的文章,给您的阅读带来不好的体验,并且干扰了您的搜索环境,非常抱歉!