Description
策策同学特别喜欢逛公园,公园可以看做有n个景点的序列,每个景点会给策策带来di 的愉悦度,策策初始有x0 的愉悦度,然而愉悦度也是有上限的,他在每个景点的愉悦度上限为Li ,策策想要从 l 到 r这一段景点中选择一段景点参观(从这一段的左端点逛到这一段的右端点),策策想知道他最终的愉悦度的最大值是多少,你能帮帮他吗?(区间可以为空,也就是说答案最小为x0 )
n,di,q<=40000,Li<=1000000
Solution
一开始还看错题了…
我们设F(l,r,x)F(l,r,x)F(l,r,x)表示从l开始走,走到r,初始值为x的最终愉悦度。
那么题目就是求max(F(u,v,x)),[u,v]∈[l,r]\max\left(F(u,v,x)\right),[u,v]\in[l,r]max(F(u,v,x)),[u,v]∈[l,r]
显然F(l,r,x)F(l,r,x)F(l,r,x)对于x是单调的,即当x1≥x2,F(l,r,x1)≥F(l,r,x2)x_1\geq x_2,F(l,r,x_1)\geq F(l,r,x_2)x1≥x2,F(l,r,x1)≥F(l,r,x2)
再设
G(l,r)=F(l,r,INF)G(l,r)=F(l,r,INF)G(l,r)=F(l,r,INF),即从第一个开始就被卡住上限。
S(l,r)=∑i=lrdiS(l,r)=\sum\limits_{i=l}^{r}d_iS(l,r)=i=l∑rdi
那么有结论,F(l,r,x)=min(G(l,r),x+S(l,r))F(l,r,x)=\min(G(l,r),x+S(l,r))F(l,r,x)=min(G(l,r),x+S(l,r))
相当于是分中途是否有卡住上限来讨论。
容易让人疑惑的是,如果第一个没被卡住,后面被卡住了,这种情况算不到。
其实不会,第一个没被卡住,后面都会被卡住,那么如果第一个被卡住,后面肯定更大,更会被卡住,因此计算结果是一样的。
有了这个结论,我们考虑分块。
对于每一个询问,两边的散块显然可以暴力计算,考虑中间的整块。
对于第j块,设它的左右边界分别为Lj,RjL_j,R_jLj,Rj
考虑计算左右端点都在第j块中的子区间贡献。
显然,对于两个子区间[u1,v1],[u2,v2][u_1,v_1],[u_2,v_2][u1,v1],[u2,v2],若S(u1,v1)>S(u2,v2)S(u_1,v_1)>S(u_2,v_2)S(u1,v1)>S(u2,v2)且G(u1,v1)>G(u2,v2)G(u_1,v_1)>G(u_2,v_2)G(u1,v1)>G(u2,v2),那[u2,v2][u_2,v_2][u2,v2]就没用了
我们将这n个子区间(n2{\sqrt n}^2n2)拉出来按照S排序,类似单调栈维护,这样G就是递减的
那么要求min(S+x,G)min(S+x,G)min(S+x,G)的最大值,直接二分G这条线和S这条线的交点(S第一个大于G的位置)就行了。
考虑跨块的情况
假设我们已经知道了第j-1块能向后贡献的最大值为C(即第j块初始开头最大是多少)
我们现在需要知道的是,结尾在第j块中的最大值,以及第j块能贡献给下一块的最大值C是多少
那么我们可以维护一个前缀单调栈和后缀单调栈,类似上面的,对于前缀单调栈,我们想找到min(S+C,G)min(S+C,G)min(S+C,G)的最大值,同样二分。
对于对下一块的贡献,要么是以C为初始走过整一块,要么是从这块中某个位置开始以x为初始,要么直接以x为初始。第一第三种情况直接计算,第二种情况同样在后缀单调栈中二分,最后三者取最大值就是对下一块的最大贡献。
时间复杂度O(nnlogn+qnlogn)O(n\sqrt n\log n+q\sqrt n\log n)O(nnlogn+qnlogn)
如果改用桶排,则可以优化到O(nn+qnlogn)O(n\sqrt n+q\sqrt n\log n)O(nn+qnlogn)
根据平衡规划的思想,如果把块大小开到nlogn\sqrt {n\log n}nlogn,那么复杂度可以到O((n+q)nlogn)O((n+q)\sqrt{n\log n})O((n+q)nlogn)
没写不知道。。。
反正直接第一种也跑的贼快
Code
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 40005
#define M 215
using namespace std;
int fr[N],q,n,a[N],n1,lim[N],sum[N],g[M][M][M],le[M],R,lp[M],ls[M];
struct node
{
int x,y;
friend bool operator <(node x,node y)
{
return (x.y<y.y)||(x.y==y.y&&x.x<y.x);
}
}d[M*M],st[M][M*M],pre[M][M],sub[M][M];
int get(int v,node *a,int len)
{
int l=1,r=len;
while(l+1<r)
{
int mid=(l+r)>>1;
if(a[mid].y+v>a[mid].x) r=mid;
else l=mid;
}
int ans=min(a[r].y+v,a[r].x);
if(r>1) ans=max(ans,min(a[r-1].y+v,a[r-1].x));
if(r>2) ans=max(ans,min(a[r-2].y+v,a[r-2].x));
return ans;
}
int main()
{
cin>>n>>q;
fo(i,1,n) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
fo(i,1,n) scanf("%d",&lim[i]);
R=sqrt(n);
int cnt=0;
n1=1;
fo(i,1,n)
{
cnt++;
if(cnt>R) fr[i]=++n1,cnt=1;
else fr[i]=n1;
}
fo(i,1,n1)
{
int l=(i-1)*R+1,r=min(i*R,n),top=0,num=0;
fo(j,l,r)
{
fo(k,j,r)
{
g[i][j-l][k-l]=(k==j)?lim[k]:min(g[i][j-l][k-l-1]+a[k],lim[k]);
d[++num].x=g[i][j-l][k-l];
d[num].y=sum[k]-sum[j-1];
}
}
sort(d+1,d+num+1);
fo(j,1,num)
{
while(top&&st[i][top].x<=d[j].x) top--;
st[i][++top]=d[j];
}
le[i]=top;
top=0;
fo(j,l,r)
{
while(top&&pre[i][top].x<=g[i][0][j-l]) --top;
pre[i][++top]=(node){g[i][0][j-l],sum[j]-sum[l-1]};
}
lp[i]=top;
top=0;
fod(j,r,l)
{
while(top&&sub[i][top].x<=g[i][j-l][r-l]) --top;
sub[i][++top]=(node){g[i][j-l][r-l],sum[r]-sum[j-1]};
}
ls[i]=top;
}
fo(i,1,q)
{
int x,y,z,ans=0;
scanf("%d%d%d",&x,&y,&z);
if(fr[x]==fr[y])
{
int v=z;
fo(j,x,y) v=max(min(lim[j],v+a[j]),z),ans=max(ans,v);
printf("%d\n",ans);
}
else
{
int v=z;
while(fr[x]==fr[x-1]) v=max(min(lim[x],v+a[x]),z),ans=max(ans,v),x++;
fo(j,fr[x],fr[y]-1)
{
int lx=(j-1)*R+1,rx=min(n,j*R);
ans=max(ans,max(get(v,pre[j],lp[j]),get(z,st[j],le[j])));
v=max(max(z,min(v+sum[rx]-sum[lx-1],g[j][0][rx-lx])),get(z,sub[j],ls[j]));
}
x=(fr[y]-1)*R+1;
fo(j,x,y) v=max(min(lim[j],v+a[j]),z),ans=max(ans,v);
printf("%d\n",ans);
}
}
}