超级钢琴
题意
给定一个序列,定义和弦为长度在[L,R]上的连续元素集,其值为元素和。求最大的前k个和弦值之和。
思路
暴力思路是显然的:枚举左端点,然后依次枚举右端点得到最大值。
考虑优化:对于每一个左端点,能得到的最大值应该为$$max{sum[j]}-sum[chosen_point]$$
求最大值的过程可以使用st表优化,然后放到堆里即可。
代码
#include <bits/stdc++.h>
using namespace std;
namespace StandardIO {
template<typename T>inline void read (T &x) {
x=0;T f=1;char c=getchar();
for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T>inline void write (T x) {
if (x<0) putchar('-'),x*=-1;
if (x>=10) write(x/10);
putchar(x%10+'0');
}
}
using namespace StandardIO;
namespace Project {
#define int long long
const int N=500500;
int n,k,L,R,ans;
int st[N][20];
int sum[N];
struct node {
int val,lpos,rpos,r;
node () {}
node (int _v,int _l,int _r,int _rr) : val(_v),lpos(_l),rpos(_r),r(_rr) {}
friend inline bool operator < (const node x,const node y) {
return x.val<y.val;
}
};
priority_queue<node> q;
inline int min (int a,int b) {
return (sum[a]<sum[b])?a:b;
}
inline int query (int l,int r) {
if (l>r) return -1;
int x=log2(r-l+1);
return min(st[l][x],st[r-(1<<x)+1][x]);
}
inline void MAIN () {
read(n),read(k),read(L),read(R);
for (register int i=1; i<=n; ++i) {
read(sum[i]),sum[i]+=sum[i-1],st[i][0]=i;
}
for (register int i=1; (1<<i)<=n; ++i) {
for (register int j=0; (1<<i)+j-1<=n; ++j) {
st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
}
}
for (register int i=L; i<=n; ++i) {
int tmp=query(max(0ll,i-R),i-L);
q.push(node(sum[i]-sum[tmp],max(i-R,0ll),i-L,i));
}
for (register int i=1; i<=k; ++i) {
node now=q.top();q.pop(),ans+=now.val;
int tmp=query(now.lpos,now.rpos),s=query(now.lpos,tmp-1),t=query(tmp+1,now.rpos);
if (s!=-1) q.push(node(sum[now.r]-sum[s],now.lpos,tmp-1,now.r));
if (t!=-1) q.push(node(sum[now.r]-sum[t],tmp+1,now.rpos,now.r));
}
write(ans);
}
#undef int
}
int main () {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
Project::MAIN();
}