P1725 琪露诺
本题有明显的阶段和状态,可以用线性dp来做。
设 f[i] 为: 到达位置 i 时最大的价值和 , 则状态转移方程如下 :
f[i]=max(f[j])+A[i] (i−R≤j≤i−L)
但是本题的N≤2e5,因此不能直接暴力枚举f[j],由于发现L和R是固定的,i-R和i-L即为一个固定的范围,因此考虑使用单调队列维护这个范围中的max(f[j])。
按上述转移方程开始递推。
最后再从左往右扫描一遍寻找最大的f[i]即可。
我使用的是数组模拟队列,如果你不会的单调队列的话,建议先查阅相应文章。
总之单调队列就是说这个队列中的数要么从小到大排列,要么从大到小排列。
而我们在此处使用的是从大到小的单调队列。
AC代码(注意一些小细节)
#include<bits/stdc++.h>
using namespace std;
int n,l,r,maxn;
int q[200010];
int a[200010],f[200010];
int main()
{
maxn=-1e9;
cin>>n>>l>>r;
for(int i=2;i<=l+1;i++)
{
f[i]=-1e9;
}
int h=1,t=0;
for(int i=1;i<=n+1;i++){
scanf("%d",&a[i]);
}
if(l==r){
int ans=0;
for(int i=1;i<=n+1;i+=l){
ans+=a[i];
}
cout<<ans<<endl;
return 0;
}
for(int i=1;i<=(n-l+1);i++){
int j=i+l;
while(h<=t && i-q[h]>=(r-l+1)) h++;
while(h<=t && f[i]>f[q[t]]) t--;
q[++t]=i;
f[j]=f[q[h]]+a[j];
}
for(int i=1;i<=n;i++){
maxn=max(maxn,f[i]);
}
cout<<maxn;
return 0;
}
GSS3 - Can you answer these queries III
思路借用 SilentEAG 大佬 与 进阶指南上的讲解。但我的代码更为通俗易懂。
需要你提供一种数据结构使之能够查询区间最大连续子段和,并且支持单点修改。
让我们来分析一下,在区间上进行操作自然而然可以想到树状数组或者是线段树。这里单点修改好办,难就难在查询上。
想一想怎么办?或者说,我们如何整理子区间的信息?
仔细思考后,我们会发现,一个区间上的连续最大和只有几种情况:
- 连续最大和的区间只在左儿子所对应的区间上。
- 连续最大和的区间只在右儿子所对应的区间上。
- 连续最大和的区间横跨左右儿子的区间。
(1)和(2)这两种情况好弄,直接继承取最值,可情况(3)呢?
除了维护区间和,区间最大连续子段和,我们还需维护紧靠左端的最大连续子段和,以及紧靠右端的最大连续子段和。
于是每次更新时,我们就可以用子区间的信息来更新当前区间了。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int SIZE=5e5+10;
int n,q,a[SIZE];
struct tree{
int l,r,dat,sum,lmax,rmax;
}e[SIZE*4];
void build(int p,int l,int r){
e[p].l=l;e[p].r=r;
if(l==r){
e[p].sum=e[p].dat=e[p].lmax=e[p].rmax=a[l];
return;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
e[p].lmax = -0x3f3f3f3f;
e[p].rmax = -0x3f3f3f3f;
e[p].dat = -0x3f3f3f3f;
e[p].sum=e[p*2].sum+e[p*2+1].sum;
e[p].lmax=max(e[p*2].lmax,e[p*2].sum+e[p*2+1].lmax);
e[p].rmax=max(e[p*2].rmax+e[p*2+1].sum,e[p*2+1].rmax);
e[p].dat=max(e[p*2].dat,max(e[p*2+1].dat,e[p*2].rmax+e[p*2+1].lmax));
}
void change(int p,int x,int v){
if(e[p].l==e[p].r){
e[p].sum=e[p].dat=e[p].lmax=e[p].rmax=v;
return;
}
int mid=(e[p].l+e[p].r)>>1;
if(x<=mid) change(p*2,x,v);
else change(p*2+1,x,v);
e[p].sum=e[p*2].sum+e[p*2+1].sum;
e[p].lmax=max(e[p*2].lmax,e[p*2].sum+e[p*2+1].lmax);
e[p].rmax=max(e[p*2].rmax+e[p*2+1].sum,e[p*2+1].rmax);
e[p].dat=max(e[p*2].dat,max(e[p*2+1].dat,e[p*2].rmax+e[p*2+1].lmax));
}
tree ask(int p,int x,int y){
if (x<=e[p].l && y>=e[p].r) return e[p];
tree a,b,c;
int ans=-0x3f3f3f3f;
a.sum=a.lmax=a.rmax=a.dat=ans;
b.sum=b.lmax=b.rmax=b.dat=ans;
c.sum=0;
int mid=(e[p].l+e[p].r)/2;
if (x<=mid){
a=ask(p*2,x,y);
c.sum+=a.sum;
}
if (y>mid){
b=ask(p*2+1,x,y);
c.sum+=b.sum;
}
c.dat=max(max(a.dat,b.dat),a.rmax+b.lmax);
c.lmax=max(a.lmax,b.lmax+a.sum);
if (x>mid) c.lmax=max(c.lmax,b.lmax);
c.rmax=max(b.rmax,a.rmax+b.sum);
if (y<=mid) c.rmax=max(c.rmax,a.rmax);
return c;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
cin>>q;
for(int i=1;i<=n;i++){
int op,x,y;
cin>>op>>x>>y;
if(op==0){
change(1,x,y);
}else{
int ans=ask(1,x,y).dat;
cout<<ans<<endl;
}
}
return 0;
}