【BZOJ2006】超级钢琴(RMQ,priority_queue)
题意:
思路:
用三元组(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 }
null