把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ2122】工作评估(分块)

题目链接

  • 给定两个长度为 \(n\) 的序列 \(b_{1\sim n}\)\(c_{1\sim n}\)
  • 定义 \(h_a(x,y)=\begin{cases}a&y=x-1,\\\min\{h_a(x,y-1)+b_y,c_y\}&y\ge x\end{cases}\)
  • \(q\) 次询问,每次给定 \(l,r,a\),求 \(\max_{l\le x\le y\le r}h_a(x,y)\)
  • \(1\le n,m\le5\times10^4\)\(0\le a_i\le10^5\)\(|b_i|\le 10^4\)

\(f(x,y,v)\)

定义 \(f(x,y,v)\) 表示一个初始值为 \(v\) 的数在经过 \([x,y]\) 中的操作后将得到的值(操作指先加 \(b_i\),再向 \(c_i\)\(\min\))。

首先有一个显然的性质:若 \(v_1 < v_2\),则 \(f(x,y,v_1)\le f(x,y,v_2)\)。因为更大的数代进去肯定不会使答案变小。

其次,可以发现这个过程中无非两种情况:

  • 曾经向某个 \(c_i\)\(\min\) 过。则最终得到的值就相当于 \(c_i\) 经过 \([i+1,y]\) 中的操作后的值,与 \(v\) 无关。故此时的取值就等于 \(f(x,y,INF)\)
  • 不曾向任何 \(c_i\)\(\min\) 过。则 \(f(x,y,v)=v+\sum_{i=x}^yb_i\)

如果向 \(c\)\(\min\) 过,则 \(v+\sum_{i=x}^yb_i\) 一定大于 \(f(x,y,INF)\);反之,若不曾取 \(\min\)\(v+\sum_{i=x}^yb_i\) 一定小于 \(f(x,y,INF)\)

也就是说,\(f(x,y,v)=\min\{f(x,y,INF),v+\sum_{i=x}^yb_i\}\)

其中 \(f(x,y,INF)\)\(\sum_{i=x}^yb_i\)\(x,y\) 确定时都是已知的(不妨分别记作 \(g\)\(s\)),也就是说 \(f(x,y,v)=\min\{g,v+s\}\)

分块处理+二分求值

对于每个块,我们直接抠出所有区间,分别求出它们的 \(g,s\)

显然,如果一个区间 \(g,s\) 全大于等于另一个区间的 \(g,s\),那么后者就是一个无用的区间。

将那些有用的区间按 \(g\) 升序,那么也就按 \(s\) 降序。

每次询问代入一个 \(v\) 得到的最大的 \(f(x,y,v)\),只需要二分找到 \(g,v+s\) 大小关系发生变化的位置 \(x\)。则小于等于 \(x\) 的部分较小值都取 \(g\),在 \(x\) 处取最大值;大于 \(x\) 的部分较小值都取 \(v+s\),在 \(x+1\) 处最大值。因此只要比较这两个位置上的值,取较大的那个即可。

这样就能求出块内答案。

然后考虑块间答案,由于代入的值越大得到的值也就越大,所以我们只需要知道经过前面的部分能够得到的最大值。

可能通过前面的最大值与当前块的一段前缀操作得到答案,只要在预处理时抠出每个块的所有前缀类似前面那样二分一下即可。

新的最大值可能是前面的最大值经过当前块所有操作。但也可能是当前块的一段后缀,只要在预处理时抠出每个块的所有后缀类似前面那样二分一下即可。

代码:\(O(n\sqrt n\log n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 50000 
#define SN 225
using namespace std;
int n,a0,nw,ans,sz,bl[N+5],b[N+5],c[N+5],t1[SN+5],t2[SN+5],t3[SN+5];
struct S {int g,s;I bool operator < (Cn S& o) Cn {return g^o.g?g<o.g:s<o.s;}}w[SN+5],f[SN+5][N+5],Pre[SN+5][SN+5],Suf[SN+5][SN+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int ff,OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0,ff=1;W(!isdigit(oc=tc())) ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));x*=ff;}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
I void Work(S* f,int& t) {RI i,c=0;for(sort(f+1,f+t+1),i=1;i<=t;++i) {W(c&&f[c].s<=f[i].s) --c;f[++c]=f[i];}t=c;}//排序,删去无用部分
I void Bd(CI o)//预处理
{
	RI l=(o-1)*sz+1,r=min(o*sz,n),i,j,g,s;
	for(i=l;i<=r;++i) for(g=1e9,s=0,j=i;j<=r;++j) g=min(g+b[j],c[j]),s+=b[j],//维护g,s
		f[o][++t1[o]]=(S){g,s},i==l&&(Pre[o][++t2[o]]=(S){g,s},0),j==r&&(Suf[o][++t3[o]]=(S){g,s},0),i==l&&j==r&&(w[o]=(S){g,s},0);//记录
	Work(f[o],t1[o]),Work(Pre[o],t2[o]),Work(Suf[o],t3[o]);//预处理出所有区间/前缀/后缀
}
I void BF(CI l,CI r) {for(RI i=l;i<=r;++i) nw=min(max(nw,a0)+b[i],c[i]),ans=max(ans,nw);}//散块暴力
I int Get(S* f,CI t,CI v) {RI l=0,r=t,mid;W(l^r) mid=l+r+1>>1,f[mid].g<v+f[mid].s?l=mid:r=mid-1;return max(l?f[l].g:0,l^t?v+f[l+1].s:0);}//二分求出代入v的最大值
I void Qry(CI o) {ans=max(ans,Get(f[o],t1[o],a0)),ans=max(ans,Get(Pre[o],t2[o],max(nw,a0))),nw=max(min(w[o].g,nw+w[o].s),Get(Suf[o],t3[o],a0));}//整块
I void Q(CI l,CI r) {RI L=bl[l],R=bl[r];if(L==R) return BF(l,r);BF(l,L*sz);for(RI i=L+1;i<R;++i) Qry(i);BF((R-1)*sz+1,r);}//询问
int main()
{
	RI Qt,i;for(read(n,Qt),i=1;i<=n;++i) read(b[i]);for(i=1;i<=n;++i) read(c[i]);
	for(sz=sqrt(n),i=1;i<=n;++i) bl[i]=(i-1)/sz+1;for(i=1;i<=bl[n];++i) Bd(i);
	RI x,y;W(Qt--) read(x,y,a0),nw=a0,ans=0,Q(x,y),writeln(ans);return clear(),0;
}
posted @ 2021-11-15 15:46  TheLostWeak  阅读(135)  评论(0编辑  收藏  举报