Description
策策同学特别喜欢逛公园,公园可以看做有n个景点的序列,每个景点会给策策带来di 的愉悦度,策策初始有x0 的愉悦度,然而愉悦度也是有上限的,他在每个景点的愉悦度上限为Li ,策策想要从 l 到 r这一段景点中选择一段景点参观(从这一段的左端点逛到这一段的右端点),策策想知道他最终的愉悦度的最大值是多少,你能帮帮他吗?(区间可以为空,也就是说答案最小为x0 )
n,di,q<=40000,Li<=1000000
Solution
一开始还看错题了…
我们设
表示从l开始走,走到r,初始值为x的最终愉悦度。
那么题目就是求
显然
对于x是单调的,即当
再设
,即从第一个开始就被卡住上限。
那么有结论,
相当于是分中途是否有卡住上限来讨论。
容易让人疑惑的是,如果第一个没被卡住,后面被卡住了,这种情况算不到。
其实不会,第一个没被卡住,后面都会被卡住,那么如果第一个被卡住,后面肯定更大,更会被卡住,因此计算结果是一样的。
有了这个结论,我们考虑分块。
对于每一个询问,两边的散块显然可以暴力计算,考虑中间的整块。
对于第j块,设它的左右边界分别为
考虑计算左右端点都在第j块中的子区间贡献。
显然,对于两个子区间 ,若 且 ,那 就没用了
我们将这n个子区间(
)拉出来按照S排序,类似单调栈维护,这样G就是递减的
那么要求
的最大值,直接二分G这条线和S这条线的交点(S第一个大于G的位置)就行了。
考虑跨块的情况
假设我们已经知道了第j-1块能向后贡献的最大值为C(即第j块初始开头最大是多少)
我们现在需要知道的是,结尾在第j块中的最大值,以及第j块能贡献给下一块的最大值C是多少
那么我们可以维护一个前缀单调栈和后缀单调栈,类似上面的,对于前缀单调栈,我们想找到 的最大值,同样二分。
对于对下一块的贡献,要么是以C为初始走过整一块,要么是从这块中某个位置开始以x为初始,要么直接以x为初始。第一第三种情况直接计算,第二种情况同样在后缀单调栈中二分,最后三者取最大值就是对下一块的最大贡献。
时间复杂度
如果改用桶排,则可以优化到
根据平衡规划的思想,如果把块大小开到
,那么复杂度可以到
没写不知道。。。
反正直接第一种也跑的贼快
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);
}
}
}