1. 程式人生 > >【BZOJ2006】超級鋼琴(RMQ,priority_queue)

【BZOJ2006】超級鋼琴(RMQ,priority_queue)

else cto parse main 元組 log 最大的 問題 mes

題意:

技術分享圖片

思路:

用三元組(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)的時間內解決。

實際上固定左端點的方法也類似,程序中使用這種方法

 1 #include<cstdio>
 2
#include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<queue> 6 #include<iostream> 7 #include<algorithm> 8 #include<set> 9 #include<map> 10 #define mp(a,b,c,d) (data){a,b,c,d} 11 #define inf 1000000000 12 #define ll long long 13 #define MAXN 510000 14
using namespace std; 15 struct data{int i,l,r,t;}; 16 17 int f[MAXN][20]; 18 int a[MAXN]; 19 20 int query(int x,int y) 21 { 22 if(x>y) return 0; 23 int len=y-x+1; int l=log(len)/log(2); 24 int s1=f[x][l]; 25 int s2=f[y-(1<<l)+1][l]; 26 if(a[s1]>a[s2]) return s1; 27 else
return s2; 28 } 29 30 bool operator<(data x,data y) 31 { 32 return a[x.t]-a[x.i-1]<a[y.t]-a[y.i-1]; 33 } 34 35 int main() 36 { 37 freopen("bzoj2006.in","r",stdin); 38 freopen("bzoj2006.out","w",stdout); 39 priority_queue<data,vector<data> >q; 40 int n,K,L,R; 41 scanf("%d%d%d%d",&n,&K,&L,&R); 42 for(int i=1;i<=n;i++) 43 { 44 scanf("%d",&a[i]); 45 a[i]+=a[i-1]; 46 f[i][0]=i; 47 } 48 int l=log(n)/log(2); 49 for(int i=1;i<=l;i++) 50 for(int j=1;j+(1<<i)-1<=n;j++) 51 { 52 int x=f[j][i-1]; int y=f[j+(1<<(i-1))][i-1]; 53 if(a[x]>a[y]) f[j][i]=x; 54 else f[j][i]=y; 55 } 56 for(int i=1;i<=n;i++) 57 if(i+L-1<=n) 58 { 59 int t=min(n,i+R-1); 60 q.push(mp(i,i+L-1,t,query(i+L-1,t))); 61 } 62 ll ans=0; 63 for(int i=1;i<=K;i++) 64 { 65 data t=q.top();q.pop(); 66 ans+=a[t.t]-a[t.i-1]; 67 //printf("%lld\n",ans); 68 if(t.t-1>=t.l) q.push(mp(t.i,t.l,t.t-1,query(t.l,t.t-1))); 69 if(t.t+1<=t.r) q.push(mp(t.i,t.t+1,t.r,query(t.t+1,t.r))); 70 } 71 printf("%lld\n",ans); 72 return 0; 73 }

【BZOJ2006】超級鋼琴(RMQ,priority_queue)