Day1
堆+RMQ
定义一个四元组(i,l,r,t)表示是以第i个为开头的,结尾在l-r之间(满足长度>=L,<=R),且结尾在t的权值和最大。
首先把以每一位开头的四元组加入堆,此时的l,r是恰好满足长度>=L,<=R;
堆中排序的关键字是权值之和。
然后把堆顶取出(i,l,r,t),然后再把(i,l,t-1,t')和(i,t+1,r,t'')加入堆中(因为要满足任意两个序列不同的条件)
那么四元组中的t如何快速求出?
用RMQ即可:维护前缀和,权值和就是sum[t]-sum[i-1],对于用一个四元组sum[i-1]相同,因此只要找到l-r中sum[i]最大的即可。
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <queue> #define mp(a,b,c,d) (data){a,b,c,d} #define inf 100000000 #define LL long long #define M 500005 struct data { int i,l,r,t; }; using namespace std; int a[M],Log[M],f[M][20],n,k,l,r; LL ans=0LL; void RMQ() { for (int i=1;i<=n;i++) f[i][0]=i; for (int j=1;(1<<j)<=n;j++) for (int i=1;i+(1<<j)-1<=n;i++) { int x1=f[i][j-1],x2=f[i+(1<<(j-1))][j-1]; f[i][j]=a[x1]>a[x2]?x1:x2; } Log[0]=-1; for (int i=1;i<=n;i++) Log[i]=Log[i>>1]+1; } inline int Query(int l,int r) { if (l==r) return l; int t=Log[r-l+1]; int x1=f[l][t],x2=f[r-(1<<t)+1][t]; return a[x1]>a[x2]?x1:x2; } inline bool operator <(data x,data y) { return a[x.t]-a[x.i-1]<a[y.t]-a[y.i-1]; } void Solve() { priority_queue<data,vector<data> > q; for (int i=1;i<=n;i++) if (i+l-1<=n) { int x=min(n,i+r-1); q.push(mp(i,i+l-1,x,Query(i+l-1,x))); } for (int i=1;i<=k;i++) { data x=q.top(); q.pop(); ans+=(a[x.t]-a[x.i-1]); if (x.t-1>=x.l) q.push(mp(x.i,x.l,x.t-1,Query(x.l,x.t-1))); if (x.t+1<=x.r) q.push(mp(x.i,x.t+1,x.r,Query(x.t+1,x.r))); } } int main() { scanf("%d%d%d%d",&n,&k,&l,&r); a[0]=0; for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]+=a[i-1]; RMQ(); Solve(); printf("%lld\n",ans); return 0; }
感悟:
1.RE是因为>>写成<<
2.感觉这道题和wc第一题有点相似。。非常巧妙~堆中每次删除一个加入两个,保证堆中元素是O(n)的