P2048 [NOI2010] 超级钢琴(RMQ 贪心)
生活随笔
收集整理的這篇文章主要介紹了
P2048 [NOI2010] 超级钢琴(RMQ 贪心)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
文章目錄
- 題目描述
- 解析
- 代碼
傳送門
題目描述
解析
首先,如果只有一個和弦,那么問題顯然簡單了
用前綴和結(jié)合ST表隨便做做即可
然而
這次要求前k大的
怎么辦呢?
參照之前有一道序列合并的做法
我們想到,可以先建一個優(yōu)先隊列,把以每個元素為開頭的最大和弦求出來
然后每次彈出隊首,再用隊首的開頭把加進來一些新的
問題是,我受那道題影響,覺得一定要加進來次大的
次大彈出就加第三大的,子子孫孫無窮匱也…
于是我就卡到這里了
----------我的思路和題解的分割線-------------
但是不必拘泥與原來的那個區(qū)間
可以把它拆開來
用三元組(st,l,r)表示st開頭,終點在[l,r]的最大值和最大值位置
那么取出一個最大值位置在pl的三元組后,可以再彈進去兩個:
(st,l,pl-1)(st,pl+1,r)
當然,要注意一些pl在邊緣的特判
這樣就迎刃而解啦
代碼
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=5e5+100; int n,m,k; int l,r; int a[N],sum[N]; struct node{int st,l,r,pl,v;bool operator < (const node y)const{return v<y.v;} }; struct node2{int v,pl; }; node2 mx[N][25]; node2 merge(node2 x,node2 y){if(x.v>y.v) return x;else return y; } int mi[N],lg[N]; void solve(){mi[0]=1;for(int i=1;i<=20;i++) mi[i]=mi[i-1]<<1;int k=0;for(int i=1;i<=n;i++){if(i>=mi[k]) k++;lg[i]=k-1;}for(int i=1;i<=n;i++) mx[i][0]=(node2){sum[i],i};for(int k=1;k<=lg[n];k++){for(int i=1;i+mi[k]-1<=n;i++){mx[i][k]=merge(mx[i][k-1],mx[i+mi[k-1]][k-1]);}}return; } node2 ask(int x,int y){int k=lg[y-x+1];return merge(mx[x][k],mx[y-mi[k]+1][k]); } priority_queue<node>q; int main(){scanf("%d%d%d%d",&n,&k,&l,&r);for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];solve();for(int i=1;i<=n;i++){if(i+l-1>n) break;int L=i+l-1,R=min(n,i+r-1);node2 x=ask(L,R);q.push((node){i,L,R,x.pl,x.v-sum[i-1]});}ll tot=0;for(int i=1;i<=k;i++){node o=q.top();q.pop();tot+=o.v;int st=o.st;if(o.pl!=o.l){node2 x=ask(o.l,o.pl-1);q.push((node){st,o.l,o.pl-1,x.pl,x.v-sum[st-1]});}if(o.pl!=o.r){node2 x=ask(o.pl+1,o.r);q.push((node){st,o.pl+1,o.r,x.pl,x.v-sum[st-1]});}}printf("%lld",tot);return 0; } /* 6 2002 4920 2003 5901 2004 2832 2005 3890 2007 5609 2008 3024 5 2002 2005 2003 2005 2002 2007 2003 2007 2005 2008 */總結(jié)
以上是生活随笔為你收集整理的P2048 [NOI2010] 超级钢琴(RMQ 贪心)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: P2168 [NOI2015] 荷马史诗
- 下一篇: 京东到家成为茅台酱香系列酒首个入驻的即时