题目
解题思路
50分可以直接暴力。
能用一个变量存储的,绝对不允许用数组!!
寻址时间卡爆!!!
这一句比调用过程快。
这题在比赛的时候已经猜测到,需要维护一个具有单调性的函数。
比赛的时候,设了
表示从i出发,一直加,知道取了min,最多能够拥有的愉悦值。
显然,
具有单调性。
但是维护这个东西没用。
考虑从直接暴走的转移过来。(转移需要"粗暴",找一段最基础的行程,即
)
设
表示初始值为
,从i走到j,最后的愉悦值是多少。
设
,则
其中,
表示
显然,要么在一个地方被
卡住了,要么没被卡。
注意到,询问的是一个区间
,即答案为
有很多的
是冗余的,寻找一个方法将它们剔除。
有一个结论是正确的,当
且
,那么
这个区间就不需要被选了。
有2种暴力:
①暴力地j++,这样会多了很多个
的状态。在50分DP的时候只会将
中最优的答案来更新j。
②考虑每个询问都暴力地将所有的
加入,根据红体字的结论,没被剔除的状态具有单调性。最后必将有一个
不增,
不减的序列。
这两种暴力显然都是对的。
那么该怎么结合?
分块。块大小为
每一块预处理删除不合法的
。
块内的贡献直接二分计算
具有单调性。
跨块的,考虑最终答案,可能是从之前的某一块开始到当前块结束,也有可能是从当前块开始到后面的某一块结束。
维护一个Y值表示前面的块给这一块的贡献。
如何计算从之前的某一块开始到当前块结束?
对于第i块,寻找
里面的最优的。维护一个前缀
的相应函数,踢掉不合法的。
如何计算从当前块开始到后面的某一块结束
从第i块开始有可能比之前的更优。
维护一个后缀
的相应函数,踢掉不合法的。
寻找
里最优的。
当然,还有一种可能。就是起点在第i块之前,终点在第i块之后。
维护一下,从上个Y走满整块到下个Y的最佳答案即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 40010
#define M 205
#define P(a) putchar(a)
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{int fi,se;};
int d[N],lim[N];
int i,j,k,n,q,sq,cs,cnt;
int sm,tot;
int L,R,x0,stk;
int l[N],r[N],bel[N],sum[N];
int F2[M][M][M];
int sip[M],sis[M],sia[M];
note pr;
note temp[N],st[N];
note pre[M][N],suf[M][N],all[M][N];
int read(){
int fh=0,rs=0;char ch=0;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh?-rs:rs;
}
void write(int x){
if(x>9)write(x/10);
P(x%10+'0');
}
int G(int L,int R){return F2[bel[L]][L-l[bel[L]]][R-l[bel[L]]];}
int ef0(int z,int x0){
int rs=0,siz=sia[z];
int L2=1,R2=siz,Mid,w=L2;
while(L2<=R2){
Mid=(L2+R2)>>1;
if(all[z][Mid].fi-all[z][Mid].se<=x0)w=Mid,L2=Mid+1;
else R2=Mid-1;
}
rs=Min(all[z][w].fi,all[z][w].se+x0);
if(w<siz)rs=Max(rs,Min(all[z][w+1].fi,all[z][w+1].se+x0));
return rs;
}
int ef1(int z,int x0){
int rs=0,siz=sip[z];
int L2=1,R2=siz,Mid,w=L2;
while(L2<=R2){
Mid=(L2+R2)>>1;
if(pre[z][Mid].fi-pre[z][Mid].se<=x0)w=Mid,L2=Mid+1;
else R2=Mid-1;
}
rs=Min(pre[z][w].fi,pre[z][w].se+x0);
if(w<siz)rs=Max(rs,Min(pre[z][w+1].fi,pre[z][w+1].se+x0));
return rs;
}
int ef2(int z,int x0){
int rs=0,siz=sis[z];
int L2=1,R2=siz,Mid,w=L2;
while(L2<=R2){
Mid=(L2+R2)>>1;
if(suf[z][Mid].fi-suf[z][Mid].se<=x0)w=Mid,L2=Mid+1;
else R2=Mid-1;
}
rs=Min(suf[z][w].fi,suf[z][w].se+x0);
if(w<siz)rs=Max(rs,Min(suf[z][w+1].fi,suf[z][w+1].se+x0));
return rs;
}
int F(int L,int R,int x0){
int res=0,i,Y;
if(bel[L]==bel[R]){
Y=x0;
fo(i,L,R){
Y=Max(x0,Min(Y+d[i],lim[i]));
res=Max(res,Y);
}
return res;
}
Y=x0;
fo(i,L,r[bel[L]]){
Y=Max(x0,Min(Y+d[i],lim[i]));
res=Max(res,Y);
}
Y=Max(Y,x0);
fo(i,bel[L]+1,bel[R]-1){
res=Max(res,ef1(i,Y));
res=Max(res,ef0(i,x0));
Y=Max(Min(Y+sum[i],G(l[i],r[i])),ef2(i,x0));
Y=Max(Y,x0);
res=Max(res,Y);
}
fo(i,l[bel[R]],R){
Y=Max(x0,Min(Y+d[i],lim[i]));
res=Max(res,Y);
}
return res;
}
bool cmp(note a,note b){
return a.fi<b.fi||(a.fi==b.fi&&a.se<b.se);
}
void chuli(int z,int X){
sort(temp+1,temp+tot+1,cmp);
int i;
stk=0;
st[++stk]=temp[i];
fo(i,2,tot){
while(stk&&temp[i].se>=st[stk].se)stk--;
st[++stk]=temp[i];
}
if(z==0){fo(i,1,stk)all[X][i]=st[i];sia[X]=stk;}else
if(z==1){fo(i,1,stk)pre[X][i]=st[i];sip[X]=stk;}else
{fo(i,1,stk)suf[X][i]=st[i];sis[X]=stk;}
}
void yuchuli(int x){
int i,x0;
fo(i,l[x],r[x]){
x0=2139062143;
fo(j,i,r[x]){
x0=Min(x0+d[j],lim[j]);
F2[x][i-l[x]][j-l[x]]=x0;
}
}
//全部
tot=0;
fo(i,l[x],r[x]){
sm=0;
fo(j,i,r[x]){
sm=sm+d[j];
temp[++tot]=(note){G(i,j),sm};
}
}
chuli(0,x);
//左
tot=0;sm=0;
fo(i,l[x],r[x]){
sm=sm+d[i];
temp[++tot]=(note){G(l[x],i),sm};
}
chuli(1,x);
//右
tot=0;sm=0;
fd(i,r[x],l[x]){
sm=sm+d[i];
temp[++tot]=(note){G(i,r[x]),sm};
}
chuli(2,x);
}
int main(){
n=read(),q=read();
fo(i,1,n)d[i]=read();
fo(i,1,n)lim[i]=read();
sq=sqrt(n);
fo(i,1,n){
bel[i]=(i-1)/sq+1;
if(!l[bel[i]])l[bel[i]]=i;
r[bel[i]]=i;
sum[bel[i]]+=d[i];
}
cnt=bel[n];
fo(i,1,cnt)yuchuli(i);
fo(cs,1,q){
L=read(),R=read(),x0=read();
write(F(L,R,x0)),P('\n');
}
return 0;
}