Description
给出一个长度为n的序列,第i天有状态值Di和评估上限Li
若当前的评估值为x0,第i天后会变成min(x0+Di,Li)
需要回答q次询问,第i次询问给出L,R,x0,求[L,R]的一个子区间[l,r]使得经过这些天之后最后的评估值最大。
n,q<=5e4
Solution
设f(l,r,x)表示若当前评估值为x,经过[l,r]这个区间会变成什么,f有两个性质
1.若a>=b,f(l,r,a)>=f(l,r,b)
2.令g(l,r)=f(l,r,inf),即在第一个位置被卡住
则f(l,r,x)=min(g(l,r),s(l,r)+x),s(l,r)表示区间[l,r]中Di的和
感受一下若被卡住一定是g(l,r),否则是s(l,r)+x,判断是否被卡住直接取min即可
然后考虑分块,注意到如果区间[l1,r1]和[l2,r2]满足g(l1,r1)>g(l2,r2)且s(l1,r1)>s(l2,r2)那么[l2,r2]肯定是没有用的
我们可以对每一块求出所有子区间的g(l,r)和s(l,r)然后将没有用的删去
那么我们得到了一个g上升s下降的序列,需要求的就是
max
l
,
r
(
m
i
n
(
g
(
l
,
r
)
,
s
(
l
,
r
)
+
x
)
)
\max_{l,r}(min(g(l,r),s(l,r)+x))
l,rmax(min(g(l,r),s(l,r)+x))
直接二分找交点判断左右两边即可
那么我们解决了在块内的情况,对于跨过块的我们可以动态维护一个y表示前i块最大能传过来的数,因为y越大f越大
那么跨块的部分直接维护一个前缀的S和G
更新y只有两种状态,一种是从前面继承过来,一种是从这个块某个位置开始走
后一种只需要再对后缀维护S和G就能更新了
复杂度
O
(
n
n
l
o
g
n
)
O(n\sqrt n log n)
O(nnlogn)
如果排序写桶排可以平衡规划
O
(
n
n
l
o
g
n
)
O(n\sqrt {nlogn})
O(nnlogn)
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int read() {
char ch;int sig=1;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar()) if (ch=='-') sig=-1;
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*sig;
}
void write(int x) {
if (!x) {puts("0");return;}
char ch[20];int tot=0;
for(;x;x/=10) ch[++tot]=x%10+'0';
fd(i,tot,1) putchar(ch[i]);
puts("");
}
const int N=4e4+5,M=205;
int n,q,pos[N],lim[N],d[N],sum[N],f[M][M][M],L[M],R[M],stk[N],tot,top;
int G(int l,int r) {
int u=pos[l];
return f[u][l-L[u]][r-L[u]];
}
int S(int l,int r) {return sum[r]-sum[l-1];}
int F(int l,int r,int x) {return min(G(l,r),S(l,r)+x);}
struct P{
int g,s;
P(int _g=0,int _s=0) {g=_g;s=_s;}
}p[N],pr[M][M],su[M][M],t[M][N];
bool cmp(P a,P b) {return a.g<b.g||a.g==b.g&&a.s<b.s;}
void get(P *pt) {
sort(p+1,p+tot+1,cmp);
top=0;
fo(i,1,tot) {
while (top&&p[i].s>=p[stk[top]].s) top--;
stk[++top]=i;
}
pt[0].s=top;
fo(i,1,top) pt[i]=p[stk[i]];
}
void pre(int x) {
fo(i,L[x],R[x]) {
f[x][i-L[x]][i-L[x]]=lim[i];
fo(j,i+1,R[x]) f[x][i-L[x]][j-L[x]]=min(f[x][i-L[x]][j-L[x]-1]+d[j],lim[j]);
}
tot=0;fo(i,L[x],R[x]) fo(j,i,R[x]) p[++tot]=P(G(i,j),S(i,j));get(t[x]);
tot=0;fo(i,L[x],R[x]) p[++tot]=P(G(L[x],i),S(L[x],i));get(pr[x]);
tot=0;fo(i,L[x],R[x]) p[++tot]=P(G(i,R[x]),S(i,R[x]));get(su[x]);
}
int calc(P *pt,int x) {
int l=1,r=pt[0].s;
while (l<r) {
int mid=l+r>>1;
if (pt[mid].g<=pt[mid].s+x) l=mid+1;
else r=mid;
}
int res=min(pt[l].g,pt[l].s+x);
res=max(res,min(pt[l-1].g,pt[l-1].s+x));
return res;
}
int go(int l,int r,int &x,int x0) {
int res=x0;
fo(i,l,r) {
x=min(max(x,x0)+d[i],lim[i]);
res=max(res,x);
}
return res;
}
int solve(int l,int r,int x) {
int u=pos[l],v=pos[r];
if (u==v) return go(l,r,x,x);
int y=x,ans=go(l,R[u],y,x);y=max(y,x);
fo(i,u+1,v-1) {
ans=max(ans,calc(t[i],x));
ans=max(ans,calc(pr[i],y));
y=F(L[i],R[i],y);
y=max(y,calc(su[i],x));
}
return max(ans,go(L[v],r,y,x));
}
int main() {
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
n=read();q=read();int m=200;
fo(i,1,n) d[i]=read(),sum[i]=sum[i-1]+d[i];
fo(i,1,n) lim[i]=read();
int sz=n/m+((n%m)>0);
fo(i,1,sz) {
L[i]=R[i-1]+1;
R[i]=min(L[i]+m-1,n);
fo(j,L[i],R[i]) pos[j]=i;
}
fo(i,1,sz) pre(i);
for(;q;q--) {
int l=read(),r=read(),x=read();
write(solve(l,r,x));
}
return 0;
}