[BZOJ 2006][NOI2010]超级钢琴(ST表+堆)
Description
小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的
音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级
和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的
所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。
我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最
大值是多少。
Solution
oimaster:
用三元组(i, l, r)表示右端点为i,左端点在[l, r]之间和最大的区间([l, r]保证是对于i可行右端点区间的一个子区间),我们用堆维护一些这样的三元组。堆中初始的元素为每个i,并且[l, r]为这个i可行左端点的区间。假如某次最大值为(i, l, r),并且j为那个和最大区间的左端点,那么需要往堆中加入两个三元组(i, l, j-1)和(i, j+1, r)。对于一个三元组,计算对应最大和的问题实际就是一个RMQ问题,可以通过Sparse Table在O(NlogN) – O(1)的时间内解决。这个算法的总时间复杂度为O(NlogN + K log(N+K))
懒得写了QwQ
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<queue> #define MAXN 500005 using namespace std; typedef long long LL; int n,k,L,R,a[MAXN],d[MAXN][23]; struct Node { int l,r,a,t; Node(int l=0,int r=0,int a=0,int t=0):l(l),r(r),a(a),t(t){} }; struct cmp { bool operator () (Node x,Node y) {return a[x.a]-a[x.t]<a[y.a]-a[y.t];} }; priority_queue<Node,vector<Node>,cmp>q; int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+c-'0';c=getchar(); } return x*f; } void RMQ_init() { for(int i=0;i<=n;i++)d[i][0]=i; for(int i=1;(1<<i)<=n;i++) { for(int j=0;j+(1<<i)-1<=n;j++) d[j][i]=a[d[j][i-1]]<a[d[j+(1<<(i-1))][i-1]]?d[j][i-1]:d[j+(1<<(i-1))][i-1]; } } int RMQ(int l,int r) { int i=0; while((1<<(i+1))<=r-l+1)i++; return a[d[l][i]]<a[d[r-(1<<i)+1][i]]?d[l][i]:d[r-(1<<i)+1][i]; } int main() { n=read(),k=read(),L=read(),R=read(); for(int i=1;i<=n;i++) a[i]=a[i-1]+read(); RMQ_init(); for(int i=L;i<=n;i++) q.push(Node(max(1,i-R+1),i-L+1,i,RMQ(max(0,i-R),i-L))); LL ans=0; while(k--) { Node tmp=q.top(); q.pop(); int p=tmp.t; ans+=a[tmp.a]-a[p]; p++; if(p-1>=tmp.l) q.push(Node(tmp.l,p-1,tmp.a,RMQ(tmp.l-1,p-2))); if(p+1<=tmp.r) q.push(Node(p+1,tmp.r,tmp.a,RMQ(p,tmp.r-1))); } printf("%lld\n",ans); return 0; }