【BZOJ2006】【NOI2010】超級鋼琴
阿新 • • 發佈:2018-11-08
題意:
Description
小Z是一個小有名氣的鋼琴家,最近C博士送給了小Z一架超級鋼琴,小Z希望能夠用這架鋼琴創作出世界上最美妙的音樂。 這架超級鋼琴可以彈奏出n個音符,編號為1至n。第i個音符的美妙度為Ai,其中Ai可正可負。 一個“超級和絃”由若干個編號連續的音符組成,包含的音符個數不少於L且不多於R。我們定義超級和絃的美妙度為其包含的所有音符的美妙度之和。兩個超級和絃被認為是相同的,當且僅當這兩個超級和絃所包含的音符集合是相同的。 小Z決定創作一首由k個超級和絃組成的樂曲,為了使得樂曲更加動聽,小Z要求該樂曲由k個不同的超級和絃組成。我們定義一首樂曲的美妙度為其所包含的所有超級和絃的美妙度之和。小Z想知道他能夠創作出來的樂曲美妙度最大值是多少。Input
第一行包含四個正整數n, k, L, R。其中n為音符的個數,k為樂曲所包含的超級和絃個數,L和R分別是超級和絃所包含音符個數的下限和上限。 接下來n行,每行包含一個整數Ai,表示按編號從小到大每個音符的美妙度。 N<=500,000 k<=500,000 -1000<=Ai<=1000,1<=L<=R<=N且保證一定存在滿足條件的樂曲Output
只有一個整數,表示樂曲美妙度的最大值。
題解:
貌似是此類套路題的開端?
一看到前k大很明顯就是要用大根堆(優先佇列)來維護,取k次堆頂即可。
區間求和可以轉化為字首和差分,那麼對於一個左端點i,能找到的最優的右端點j就是在區間$[i-1+L,i-1+R]$中$pre[j]$最大的j;
顯然是RMQ問題,用ST表可以做到$O(1)$
用一個四元組$(i,t,l,r)$來表示一個超級和絃,表示左端點是i,右端點區間為$[l,r]$,最優右端點為t;
先加入初始的n個最優的和絃,每次取完堆頂把右端點區間以t為中心分裂成兩個重新加入佇列,統計答案即可。
注意最後的答案要用longlong
程式碼:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #define inf 2147483647
8 #define eps 1e-9
9 using namespace std;
10 typedef long long ll;
11 int n,k,l,r,a[500001],pre[500001],lg[500001],st[500001][20];
12 ll ans;
13 struct node{
14 int t1,t2,l,r;
15 friend bool operator <(node x,node y){
16 return pre[x.t2]-pre[x.t1-1]<pre[y.t2]-pre[y.t1-1];
17 }
18 };
19 priority_queue<node>q;
20 int query(int l,int r){
21 if(l==r)return l;
22 return (pre[st[l][lg[r-l+1]]]>pre[st[r-(1<<lg[r-l+1])+1][lg[r-l+1]]])?st[l][lg[r-l+1]]:st[r-(1<<lg[r-l+1])+1][lg[r-l+1]];
23 }
24 int main(){
25 scanf("%d%d%d%d",&n,&k,&l,&r);
26 lg[1]=0;
27 for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
28 for(int i=1;i<=n;i++)st[i][0]=i;
29 for(int i=1;i<=n;i++){
30 scanf("%d",&a[i]);
31 pre[i]=pre[i-1]+a[i];
32 }
33 for(int j=1;j<=19;j++){
34 for(int i=1;i+(1<<j)-1<=n;i++){
35 st[i][j]=(pre[st[i][j-1]]>pre[st[i+(1<<j-1)][j-1]])?st[i][j-1]:st[i+(1<<j-1)][j-1];
36 }
37 }
38 for(int i=1;i<=n;i++){
39 if(i+l-1<=n){
40 q.push((node){i,query(i+l-1,min(i+r-1,n)),i+l-1,min(i+r-1,n)});
41 }
42 }
43 for(int i=1;i<=k;i++){
44 node now=q.top();
45 q.pop();
46 ans+=pre[now.t2]-pre[now.t1-1];
47 if(now.t2-1>=now.l)q.push((node){now.t1,query(now.l,now.t2-1),now.l,now.t2-1});
48 if(now.t2+1<=now.r)q.push((node){now.t1,query(now.t2+1,now.r),now.t2+1,now.r});
49 }
50 printf("%lld",ans);
51 return 0;
52 }