BZOJ2006:[NOI2010]超级钢琴——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=2006
https://www.luogu.org/problemnew/show/P2048#sub
小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。
这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。
一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最大值是多少。
O(n^2)很好想,肯定会T。
但事实上我们只需要区间前k大即可,所以没必要算出O(n^2)的区间和。
思考对于每个左端点l,有一个r使得[l,r]区间和最大,我们把这些值都存起来取最大就是所有区间和的最大值,然后把它弹出。
继续对于弹出的元素也可以贡献它的第二大,第三大……分别满足的r,我们依次加进去。
一种直观的想法是主席树维护,然后翻了题解开始舔st表。
事实上我们在[L,R]找到的最大值对应的r,其第二大对应的r一定是[L,r-1]和[r+1,R]最大值对应的r,用st表维护一遍即可。
(然而我菜……我根本没想到第三段话的内容TAT)
(果然人傻就该多刷题)
#include<cmath> #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int N=5e5+5; const int B=19; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int n,k,L,R; int s[N],lg[N],dp[N][B+3]; struct data{ int l,r,w,L,R; bool operator <(data b)const{ return w<b.w; } }; priority_queue<data>q; inline int qpow(int a){return 1<<a;} void st(){ for(int i=1;i<=n;i++){ dp[i][0]=i; lg[i]=lg[i-1]; if((1<<lg[i]+1)==i)lg[i]++; } for(int j=1;j<=lg[n];j++){ for(int i=1;i<=n;i++){ if(i+qpow(j)-1>n)break; int l=dp[i][j-1],r=dp[i+qpow(j-1)][j-1]; if(s[l]>s[r])dp[i][j]=l; else dp[i][j]=r; } } } inline int getmax(int l,int r){ int len=r-l+1,h=lg[len]; int l1=dp[l][h],r1=dp[r-qpow(h)+1][h]; if(s[l1]>s[r1])return l1; else return r1; } int main(){ n=read(),k=read(),L=read(),R=read(); for(int i=1;i<=n;i++)s[i]=s[i-1]+read(); st(); for(int i=1;i+L-1<=n;i++){ int j=getmax(i+L-1,min(i+R-1,n)); q.push((data){i,j,s[j]-s[i-1],i+L-1,min(i+R-1,n)}); } ll ans=0; while(k--){ data tmp=q.top();q.pop(); ans+=tmp.w; int i=tmp.l,j=tmp.r; if(tmp.L<=j-1){ int t=getmax(tmp.L,j-1); q.push((data){i,t,s[t]-s[i-1],tmp.L,j-1}); } if(j+1<=tmp.R){ int t=getmax(j+1,tmp.R); q.push((data){i,t,s[t]-s[i-1],j+1,tmp.R}); } } printf("%lld\n",ans); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++