1 条题解
-
0

#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
- 上传者