[题解]NOI2010超级钢琴
给定一个l,r,表示只能选择起点为i,终点在i+l-1,i+r-1内的区间,每个区间的贡献为区间内的和,现在从所有合法区间内选k个使和最大
先转化成前缀和,要求的是$max(sum[k]-sum[i-1])(1 \le i \le n)(i+l-1 \le k \le i+r-1)$,可以看到sum[i-1]是定值,这样对于一个点只要sum[k]最大即可,用st表可以实现,前k大我们可以用堆取k次,
但是取完sum[k]后可能还有次大的k之类的,所以每次我们取完一个点时把它分裂成$(i,l,k-1),(i,k+1,r)$
常用处理方法,堆贪心每次取完后扩展出可能优的状态,前k大取k次
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=500009; int n,k,L,R; ll sum[maxn],st[maxn][21],A; void pre(){ for(int i=1;i<=n;i++)st[i][0]=i; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++){ if(sum[st[i][j-1]]>sum[st[i+(1<<(j-1))][j-1]])st[i][j]=st[i][j-1]; else st[i][j]=st[i+(1<<(j-1))][j-1]; } } int query(int l,int r){ int k=log2(r-l+1); if(sum[st[l][k]]>sum[st[r-(1<<k)+1][k]])return st[l][k]; else return st[r-(1<<k)+1][k]; } struct node{ int x,l,r,t; node(){} node(int xx,int Ll,int rr){ x=xx;l=Ll;r=rr; t=query(l,r); } bool operator <(const node&tt)const{ return sum[t]-sum[x-1]<sum[tt.t]-sum[tt.x-1]; } }; priority_queue<node>q; int main(){ scanf("%d%d%d%d",&n,&k,&L,&R); for(int i=1;i<=n;i++)scanf("%lld",&A),sum[i]=sum[i-1]+A; pre(); for(int i=1;i<=n;i++) if(i+L-1<=n)q.push(node(i,i+L-1,min(i+R-1,n))); ll ans=0; while(k--){ int x=q.top().x,l=q.top().l,r=q.top().r,t=q.top().t; q.pop(); ans+=sum[t]-sum[x-1]; if(l!=t)q.push(node(x,l,t-1)); if(r!=t)q.push(node(x,t+1,r)); } printf("%lld",ans); }