解析
首先想想 O(nm)O(nm)O(nm) 怎么做。
从左往右扫,不断把当前值和 x0x_0x0 取 max\maxmax 即可。
考虑正解:
设 f(l,r,w)f(l,r,w)f(l,r,w) 为初始为 www,工作区间为 (l,r)(l,r)(l,r) 结束后的价值,s(l,r)=∑i=lrais(l,r)=\sum_{i=l}^ra_is(l,r)=∑i=lrai。
那么就有:
f(l,r,w)=min(f(l,r,inf),w+s(l,r))f(l,r,w)=\min(f(l,r,inf),w+s(l,r))f(l,r,w)=min(f(l,r,inf),w+s(l,r))
如果一直没有碰到,显然就是 w+s(l,r)w+s(l,r)w+s(l,r)。
否则就会和 f(l,r,inf)f(l,r,inf)f(l,r,inf) 相同。考虑反证:最终结果不同,那么最后触顶位置必然不同。设两个过程 A,BA,BA,B 最后触顶位置分别为 x,y(x<y)x,y(x<y)x,y(x<y) 那么 BBB 必然在 xxx 处没有触顶(否则 A、BA、BA、B 以后就一样了),那么在 xxx 处就有 A>BA>BA>B,那么以后必然始终有 A≥BA\ge BA≥B。然而后来到 yyy 的地方 BBB 触顶而 AAA 没有,有了 A<BA<BA<B,矛盾。
分块后,我们可以块内暴力计算出所有区间的 f(l,r,inf),s(l,r)f(l,r,inf),s(l,r)f(l,r,inf),s(l,r)。
注意到,如果某个区间的两个特征值都不超过另一个区间,那么其必然是无用的。所以我们可以去掉所有无用区间,只剩下 s,fs,fs,f 反向单调的一些区间。
如果两端点在块内,我们就可以在块内的这些区间中二分找到 f+w,sf+w,sf+w,s 大小关系改变的分界点,那么最大值必然是分界点两侧的区间之一。
对于跨块的情况,我们采用和 O(nm)O(nm)O(nm) 暴力类似的思想,一块一块扫,尝试维护当前的最大值。
可以分为以下3种情况:
- 从左侧块外开始,到块内结束。
- 从左侧块外开始,延伸到右侧块外。
- 从块内开始,延伸到右侧块外。
采用和块内类似的思想,分别再对每一个块处理出所有有用的前缀区间、后缀区间即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
const int N=1e5+100;
const int B=500;
const int inf=1e9;int n,m,w;
int bel[N],st[B],ed[B],tot;
int a[N],lim[N],sum[N];
struct node{int s,g;bool operator < (const node oth)const{if(s!=oth.s) return s<oth.s;else return g<oth.g;}
};
node pre[B][B],suf[B][B],c[B][N];
int num_pre[B],num_suf[B],num_c[B];
int s[B],g[B];
void init_block(){w=sqrt(n);tot=(n+w-1)/w;for(int i=1;i<=n;i++) bel[i]=(i+w-1)/w;for(int i=1;i<=tot;i++) st[i]=(i-1)*w+1,ed[i]=min(n,i*w);return;
}
node tmp[N];
int calc(node *x,int num){for(int i=1;i<=num;i++) tmp[i]=x[i];sort(tmp+1,tmp+1+num);int top(0);for(int i=1;i<=num;i++){while(top&&x[top].g<=tmp[i].g) --top;x[++top]=tmp[i];}return top;
}void solve(int k){int num(0),num1(0),num2(0);for(int l=st[k];l<=ed[k];l++){int now=inf,ss(0);for(int r=l;r<=ed[k];r++){now=min(lim[r],now+a[r]);ss+=a[r];c[k][++num]=(node){ss,now};if(l==st[k]) pre[k][++num1]=(node){ss,now};if(r==ed[k]) suf[k][++num2]=(node){ss,now};if(l==st[k]&&r==ed[k]) s[k]=ss,g[k]=now;}}num_c[k]=calc(c[k],num);num_pre[k]=calc(pre[k],num1);num_suf[k]=calc(suf[k],num2);
}
void init(){init_block();for(int i=1;i<=tot;i++) solve(i);
}
int find(node *x,int num,int w){int st=1,ed=num;while(st<ed){int mid=(st+ed+1)>>1;if(x[mid].s+w<=x[mid].g) st=mid;else ed=mid-1;}int ans=min(x[st].s+w,x[st].g);if(st<num){++st;ans=max(ans,min(x[st].s+w,x[st].g));}return ans;
}signed main(){
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endifn=read();m=read();for(int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i];for(int i=1;i<=n;i++) lim[i]=read();init();while(m--){int l=read(),r=read(),w=read();int x=bel[l],y=bel[r],cur=w,ans(0);if(x==y){for(int i=l;i<=r;i++){cur=max(w,min(lim[i],cur+a[i]));ans=max(ans,cur);}printf("%d\n",ans);}else{for(int i=l;i<=ed[x];i++){cur=max(w,min(lim[i],cur+a[i]));ans=max(ans,cur);}for(int i=x+1;i<y;i++){ans=max(ans,find(c[i],num_c[i],w));ans=max(ans,find(pre[i],num_pre[i],cur));cur=max(w,max(min(cur+s[i],g[i]),find(suf[i],num_suf[i],w)));ans=max(ans,cur);}for(int i=st[y];i<=r;i++){cur=max(w,min(lim[i],cur+a[i]));ans=max(ans,cur);}printf("%d\n",ans);}}return 0;
}
/*
58 14 5762*/