题面:
思路:
首先容易想到用堆维护的O(n2logn)暴力
那么肯定就是在这个基础上套数据结构了【愉快】
然而我因为过于蒟蒻......只想得到主席树暴力***过去的方法
大概就是把前缀和算出来,然后放到一棵线段树里面
对于每一个i(i=1...(n-L+1)),线段树查询以i为左端点的所有区间右端点中,前缀和最大的一个
因为区间[i,j]可以变成pre[j]-pre[i]的形式,那么只需要最大化pre[j]就可以了
一开始,对于所有的i把这个最大值求出来,减掉pre[i-1],再放到堆里面
每次从堆顶取出当前最大值,然后答案加上这个值,同时主席树上修改一波,把这个点在树上的值改成-inf
其实一共有n棵主席树,只不过每一棵都公用同一个初始树,后面每次修改又只会新开logn的节点,所以总空间复杂度应该是O(nlogn+klogn)的
就这样,总时间复杂度O(nlogn+klogn),常数略大
然而,菊苣们早已开发出了新的希望算法,那就是查询O(1)的ST表
把上文中的初始询问(n个,在初始线段树上的那些)转化成三元组(i,L,R),代表左端点是i的,长度在[L,R]之内的序列最大值
那么,每次堆顶弹出以后,可以这样操作:
设堆顶元素为x,我们新建两个三元组(i,L,x-1)(i,x+1,R),把这两个三元组的值求出来放到堆里面去即可
效率虽然还是O(nlogn+klogn),但是常数比我的算法不知道高到哪里去了,跑的贼快
Code:
不知道为什么,500000的点需要开两千五百万的数组......连续3次RE
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #define inf 1e15 7 #define ll long long 8 using namespace std; 9 inline int read(){ 10 int re=0,flag=1;char ch=getchar(); 11 while(ch>‘9‘||ch<‘0‘){ 12 if(ch==‘-‘) flag=-1; 13 ch=getchar(); 14 } 15 while(ch>=‘0‘&&ch<=‘9‘) re=(re<<1)+(re<<3)+ch-‘0‘,ch=getchar(); 16 return re*flag; 17 } 18 struct node{ 19 ll w;int pos; 20 bool operator <(const node &b){ 21 return w<b.w; 22 } 23 }a[25000010]; 24 struct NODE{ 25 ll w;int pos1,pos2; 26 bool operator <(const NODE &b)const{ 27 return w<b.w; 28 } 29 }; 30 int n,m,L,R,x[500010]; 31 int ch[25000010][2],cnt,root[500010];ll pre[500010]; 32 priority_queue<NODE>q; 33 void update(int x){ 34 if(a[ch[x][0]].w>=a[ch[x][1]].w) a[x].w=a[ch[x][0]].w,a[x].pos=a[ch[x][0]].pos; 35 else a[x].w=a[ch[x][1]].w,a[x].pos=a[ch[x][1]].pos; 36 } 37 int build(int l,int r){ 38 int mid=(l+r)>>1,cur=++cnt; 39 if(l==r){ 40 a[cur].w=pre[l];a[cur].pos=l;return cur; 41 } 42 ch[cur][0]=build(l,mid);ch[cur][1]=build(mid+1,r); 43 update(cur); 44 return cur; 45 } 46 node max(node l,node r){ 47 return l<r?r:l; 48 } 49 node query(int l,int r,int ql,int qr,int cur){ 50 int mid=(l+r)>>1;node re=(node){-inf,0}; 51 if(l>=ql&&r<=qr) return a[cur]; 52 if(mid>=ql) re=max(re,query(l,mid,ql,qr,ch[cur][0])); 53 if(mid<qr) re=max(re,query(mid+1,r,ql,qr,ch[cur][1])); 54 return re; 55 } 56 int change(int l,int r,int u,int his){ 57 int mid=(l+r)>>1,cur=++cnt; 58 if(l==r){ 59 a[cur]=(node){-inf,l};return cur; 60 } 61 if(u<=mid) ch[cur][1]=ch[his][1],ch[cur][0]=change(l,mid,u,ch[his][0]); 62 else ch[cur][0]=ch[his][0],ch[cur][1]=change(mid+1,r,u,ch[his][1]); 63 update(cur);return cur; 64 } 65 int main(){ 66 freopen("piano.in","r",stdin); 67 freopen("piano.out","w",stdout); 68 int i,t1;ll ans=0;node tmp;NODE tt; 69 n=read();m=read();L=read();R=read(); 70 for(i=1;i<=n;i++) x[i]=read(),pre[i]=pre[i-1]+(ll)x[i]; 71 t1=build(1,n); 72 for(i=1;i<=n-L+1;i++){ 73 root[i]=t1;tmp=query(1,n,min(n,i+L-1),min(n,i+R-1),t1); 74 q.push((NODE){-pre[i-1]+tmp.w,i,tmp.pos}); 75 } 76 for(i=1;i<=m;i++){ 77 ans+=q.top().w;tt=q.top();q.pop(); 78 root[tt.pos1]=change(1,n,tt.pos2,root[tt.pos1]); 79 tmp=query(1,n,min(n,tt.pos1+L-1),min(n,tt.pos1+R-1),root[tt.pos1]); 80 q.push((NODE){-pre[tt.pos1-1]+tmp.w,tt.pos1,tmp.pos}); 81 } 82 printf("%lld\n",ans); 83 }