【BZOJ 2006】[NOI2010]超级钢琴 ST
我们先把所有最左端对应的最优右端入堆,eg: z 在[l,r](由题目给出的L,R决定)之间的最优解 y,然后出堆以后,再入堆z,y-1,z,y+1,那么我们只需要用st找最大前缀和就好了(ST是一种用来解决RMQ问题的方法他的应用也就限于此了)
#include <cstdio> #include <cstring> #include <queue> #define make(a,b,c,d) (DT){a,b,c,d} #define MAXN 500000 using namespace std; int bin[20],st[MAXN+10][20],Log[MAXN+10]; int n,k,L,R,s[MAXN+10],a[MAXN+10]; long long ans; struct DT { int z,l,r,y; bool operator < (const DT next)const { return s[next.y]-s[next.z-1]>s[y]-s[z-1]; } }; inline int Min(int x,int y) { return x<y?x:y; } inline void pre() { bin[0]=1;for(register int i=1;i<20;i++)bin[i]=bin[i-1]<<1; Log[0]=-1;for(register int i=1;i<=n;i++)Log[i]=Log[i>>1]+1; for(register int i=1;i<=n;i++)st[i][0]=i; for(register int i=1;i<20;i++) for(register int j=1;j<=n;j++) if(j+bin[i]-1<=n) st[j][i]=s[st[j][i-1]]>s[st[j+bin[i-1]][i-1]]?st[j][i-1]:st[j+bin[i-1]][i-1]; } priority_queue<DT> Q; inline int query(int x,int y) { register int len=Log[y-x+1]; return s[st[x][len]]>s[st[y-bin[len]+1][len]]?st[x][len]:st[y-bin[len]+1][len]; } inline void Init() { scanf("%d%d%d%d",&n,&k,&L,&R); for(register int i=1;i<=n;i++)scanf("%d",&a[i]); for(register int i=1;i<=n;i++)s[i]=s[i-1]+a[i]; pre(); for(register int i=1,r;i<=n;i++) if(i+L-1<=n) r=Min(n,i+R-1),Q.push(make(i,i+L-1,r,query(i+L-1,r))); } inline void Work() { while(k--) { DT x=Q.top();Q.pop(); ans+=s[x.y]-s[x.z-1]; if(x.y-1>=x.l)Q.push(make(x.z,x.l,x.y-1,query(x.l,x.y-1))); if(x.y+1<=x.r)Q.push(make(x.z,x.y+1,x.r,query(x.y+1,x.r))); } printf("%lld",ans); } int main() { Init(); Work(); return 0; }
苟利国家生死以, 岂因祸福避趋之。