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;
}
致歉:笔者已经意识到这是一篇几乎没有价值的文章,给您的阅读带来不好的体验,并且干扰了您的搜索环境,非常抱歉!

浙公网安备 33010602011771号