bzoj2006: [NOI2010]超级钢琴

xiaoxiao2025-08-10  28

题目 题解

Solution

对于任意区间右端点 i i i,其左端点取值在 l l l, r r r之间,若左端点为 m m m,则 v v v m a x ( s u m [ i ] − s u m [ m − 1 ] ) max(sum[i]-sum[m-1]) max(sum[i]sum[m1]),显然这里 i i i是不变的,所以可以用 s t st st表查询 m m m的位置,然后计算v。 现将所有右端点扫一遍,然后扔到堆里面,堆中节点记录的是决策,即右端点 i i i,左端点区间,优先级由 v v v决定 然后取出堆顶, v v v加到 a n s ans ans里去,分裂 [ l , r ] [l,r] [l,r] [ l , m − 1 ] [l,m-1] [l,m1] [ m + 1 , r ] [m+1,r] [m+1,r] R M Q RMQ RMQ出新的 v v v,再将分裂后的区间加到堆里 重复 k k k

Code

#include<bits/stdc++.h> using namespace std; const int N=500002; struct node{ int i,l,m,r,v; friend bool operator <(node a,node b){return a.v<b.v;} }t; priority_queue<node>q; int lg[N],L,R,i,j,n,k,a[N],tmp,st[N][20],l,r,m; long long ans; inline char gc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int rd(){ int x=0,fl=1;char ch=gc(); for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1; for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48); return x*fl; } int get(int x,int y){ x--;y--; int l=lg[y-x+1]; if (a[st[x][l]]<a[st[y-(1<<l)+1][l]]) return st[x][l]+1; return st[y-(1<<l)+1][l]+1; } int main(){ n=rd();k=rd();L=rd();R=rd(); lg[0]=-1; for (i=1;i<=n;i++) a[i]=rd()+a[i-1],lg[i]=lg[i>>1]+1,st[i][0]=i; for (j=1;j<=lg[n];j++) for (i=0;i+(1<<j)-1<=n;i++) if (a[st[i][j-1]]<a[st[i+(1<<j-1)][j-1]]) st[i][j]=st[i][j-1]; else st[i][j]=st[i+(1<<j-1)][j-1]; for (i=1;i<=n;i++){ l=max(i-R+1,1);r=max(i-L+1,1); if (i-l+1<L) continue; m=get(l,r); q.push((node){i,l,m,r,a[i]-a[m-1]}); } while (k--){ t=q.top();q.pop(); ans+=t.v; l=t.l;r=t.r;m=t.m; if (l<m){ tmp=get(l,m-1); q.push((node){t.i,l,tmp,m-1,a[t.i]-a[tmp-1]}); } if (m<r){ tmp=get(m+1,r); q.push((node){t.i,m+1,tmp,r,a[t.i]-a[tmp-1]}); } } printf("%lld",ans); }
转载请注明原文地址: https://www.6miu.com/read-5034586.html

最新回复(0)