1 条题解

  • 0
    @ 2025-11-16 16:20:17

    #include<bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define maxn 50005
    #define maxs 225
    #define maxb 305
    using namespace std;
    struct val{
    	long long g;
    	long long s;
    	val(){
    		
    	}
    	val(long long _g,long long _s){
    		g=_g;
    		s=_s;
    	}
    	friend bool operator < (val p,val q){
    		if(p.g==q.g) return p.s<q.s;
    		else return p.g<q.g;
    	}
    };
    int n,m;
    int sz;
    int cnt;
    long long d[maxn],lim[maxn]; 
    long long g[maxs][maxs][maxs];
    //g[i][l][r]表示第i块中[l,r]区间的g值,其中l,r是块内坐标,实际下标要加上lb(i) 
    long long sum[maxn];//前缀和
    int id[maxn];//id[i]表示第i个位置属于第几个块
    inline int lb(int id){//lb(id),rb(id)为第i个块的左右边界
    	return (id-1)*sz+1;
    }
    inline int rb(int id){
    	return id*sz>=n?n:id*sz;
    }
    inline long long get_s(int l,int r){
    	return sum[r]-sum[l-1];	
    }
    inline long long get_g(int l,int r){
    	int k=id[l];
    	return g[k][l-lb(k)][r-lb(k)];
    }
     
    vector<val>vblock[maxb],vright[maxb],vleft[maxb];
    //vblock存当前块内g值
    //vright存以当前块中位置为终点的值,查询时右边多出部分 
    //vleft存以当前块中位置为起点的值,查询时左边多出部分 
    void del_small(vector<val> &in){
    	sort(in.begin(),in.end());
    	stack<val>s; //单调栈
    	for(int i=0;i<in.size();i++){
    		while(!s.empty()&&s.top().s<in[i].s) s.pop();
    		s.push(in[i]);
    	}
    	in.clear();
    	while(!s.empty()){
    		in.push_back(s.top());
    		s.pop();
    	}
    }
     
    void init(int id,int l,int r){//对每个块预处理,id为块编号
    	for(int i=l;i<=r;i++){//暴力dp
    		long long v=INF;
    		for(int j=i;j<=r;j++){
    			v=min(v+d[j],lim[j]);
    			g[id][i-l][j-l]=v;
    		}
    	}
    	vright[id].clear();
    	for(int i=l;i<=r;i++){
    		vright[id].push_back(val(get_g(l,i),get_s(l,i)));
    	}
    	del_small(vright[id]);
    	vblock[id].clear();
    	for(int i=l;i<=r;i++){
    		for(int j=i;j<=r;j++){
    			vblock[id].push_back(val(get_g(i,j),get_s(i,j)));
    		}
    	}
    	del_small(vblock[id]);
    	vleft[id].clear();
    	for(int i=l;i<=r;i++){
    		vleft[id].push_back(val(get_g(i,r),get_s(i,r)));
    	}
    	del_small(vleft[id]);
     
    }
     
    long long find_mpos(vector<val> &v,long long x0){
    	//求之前的值x0,再加上v数组中的最大值之后的答案 
    	//s单调递增,g单调递减,二分找到交点 
    	int l=0,r=v.size()-1,mid,ans=0;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(v[mid].g>=v[mid].s+x0){
    			ans=mid;
    			l=mid+1;
    		}else r=mid-1;
    	}
    	long long res=0;
    	//可能ans,ans+1在交点两侧,取max 
    	if(ans+1<v.size()) //注意边界
           res=max(min(v[ans].g,v[ans].s+x0),min(v[ans+1].g,v[ans+1].s+x0));
    	else res=min(v[ans].g,v[ans].s+x0);
    	return res;
    }
     
    long long query(int l,int r,long long x0){//查询l,r,x0
    	long long ans=x0,tmp;//tmp为从l到当前位置的答案 ,ans表示目前最优答案
    	tmp=x0;
    	for(int i=l;i<=min(rb(id[l]),r);i++){//不完整块的暴力
    		tmp=min(max(tmp,x0)+d[i],lim[i]);
    		ans=max(ans,tmp);
    	}
    	for(int i=id[l]+1;i<id[r];i++){
    		ans=max(ans,find_mpos(vright[i],tmp));
            //find_mpos(v,x0)是按上述方法求v中最大值,且加上x0
            //从前面的块走过来,从本块结束,把tmp当成x0传进去,相当于把这块之前的答案也累计进去,再加上v里面的部分即是从前面到本块的答案
    		ans=max(ans,find_mpos(vblock[i],x0));
            //从本块开始,从本块结束
    		tmp=min(get_g(lb(i),rb(i)),get_s(lb(i),rb(i))+tmp);
            //从这一块前面开始走到这一块后面,不在本块结束,所以不更新ans,把本块的g值和s值累计入tmp
    		tmp=max(tmp,find_mpos(vleft[i],x0));
            //从本块开始,走到块尾
            
            //从这一块前面开始走到这一块后面,不在本块结束这种情况的值已经存在tmp里了,等到了结束的地方再更新ans,这里不用写
    	}
    	if(id[l]!=id[r]){
    		for(int i=lb(id[r]);i<=r;i++){//不完整块的暴力
    			tmp=min(max(tmp,x0)+d[i],lim[i]);
    			ans=max(ans,tmp);
    		}
    	}
    	return ans;
    }
     
    int main(){
    	int l,r;
    	long long x0;
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&d[i]); 
    		sum[i]=sum[i-1]+d[i];
    	}
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&lim[i]); 
    	}
    	sz=sqrt(n);
    	cnt=1;
    	for(int i=1;i<=n;i++){
    		id[i]=cnt;
    		if(i%sz==0) cnt++;
    	}
    	for(int i=1;i<=cnt;i++){
    		init(i,lb(i),rb(i));
    	}
    	for(int i=1;i<=m;i++){
    		scanf("%d %d %lld",&l,&r,&x0);
    		printf("%lld\n",query(l,r,x0));
    	}
    }
    
    • 1

    信息

    ID
    3787
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者