Gym102586B Evacuation
Link
对于\(f(l,r,x)\),如果我们固定了\(x\),那么构造最优的\(b\)的方法是很简单的:
从小到大枚举\(i\in[0,+\infty)\),尽可能地让\(b_{x-i},b_{x+i}\)大。
如果在\(S\)还没有放完的时候遇到了一个\(pos\notin[l,r]\),那么把剩下的\(S\)全部放到\(b_{pos}\)就行了。
若\(x\in[l,mid]\)那么\(pos=l-1\),若\(x\in(mid,r]\)那么\(pos=r+1\)。
不难发现\(x\in[l,mid]\)时\(f(l,r,x)\)与\(r\)无关,\(x\in(mid,r]\)时\(f(l,r,x)\)与\(l\)无关。
因此设\(f(l,r,x)=\begin{cases}f(l,x)&x\in[l,mid]\\g(r,x)&x\in(mid,r]\end{cases}\)
预处理\(\{a_i\}\)的前缀和以及\(\{ia_i\}\)的前缀和之后可以\(O(1)\)的求出单点\(f,g\)。
观察可得\(f,g\)满足四边形不等式。
考虑离线所有询问,对于一个形如\(\max\limits_{x\in[L,R]}(f\text{ or }g)(r,x)\)的询问,将其拆分成线段树上的\(O(\log n)\)个区间,最后对线段树的每个节点用决策单调性优化暴力枚举\(x\)计算即可。
时间复杂度是\(O(n\log^2n)\),利用基数排序可以做到\(O(n\log n)\)。
#include<cctype>
#include<cstdio>
#include<vector>
#include<utility>
#include<algorithm>
char ibuf[1<<23|1],*iS=ibuf;
using i64=long long;
using pi=std::pair<int,int>;
const int N=200007;
int n,q;i64 s,s1[N],s2[N],ans[N];int bound[N];
std::vector<pi>trans[4*N][2];
i64 read(){i64 x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
i64 sum(i64*s,int l,int r){return s[r]-s[l-1];}
i64 cal(int pos,int r)
{
return r<=0? 0:pos*(sum(s1,pos-r,pos-1)-sum(s1,pos+1,pos+r))-(sum(s2,pos-r,pos-1)-sum(s2,pos+1,pos+r));
}
i64 cal(int o,int pos,int gap)
{
int f=o? 1:-1,t=o? std::min(gap+1,pos+bound[pos]+1):std::max(gap-1,pos-bound[pos]-1);
return cal(pos,f*(t-pos)-1)+f*(t-pos)*(s-(o? sum(s1,2*pos-t+1,t-1):sum(s1,t+1,2*pos-t-1)));
}
void work(int o,int l,int r,std::vector<pi>&q)
{
if(q.empty()) return ;
if(l==r){for(auto[pos,id]:q)ans[id]=std::max(ans[id],cal(o,l,pos));return;}
std::vector<pi>L,R;int lim=(int)q.size()/2,pos=0;i64 t,mx=0;
for(int i=0;i<(int)q.size();++i) if(i^lim) (i<lim? L:R).push_back(q[i]);
for(int i=l;i<=r;++i) if((t=cal(o,i,q[lim].first))>mx) mx=t,pos=i;
ans[q[lim].second]=std::max(ans[q[lim].second],mx),work(o,l,pos,L),work(o,pos,r,R);
}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)/2)
void update(int p,int l,int r,int L,int R,int o,int pos)
{
if(L>r||l>R||L>R) return ;
if(L<=l&&r<=R) return trans[p][o].emplace_back(o? R:L,pos),void();
update(ls,l,mid,L,R,o,pos),update(rs,mid+1,r,L,R,o,pos);
}
void solve(int p,int l,int r)
{
if(l^r) solve(ls,l,mid),solve(rs,mid+1,r);
for(int i=0;i<2;++i) std::sort(trans[p][i].begin(),trans[p][i].end()),work(i,l,r,trans[p][i]);
}
#undef ls
#undef rs
int find(int pos)
{
int l=0,r=std::min(n-pos,pos-1),ans=-1;
while(l<=r) sum(s1,pos-mid,pos+mid)<s? ans=mid,l=mid+1:r=mid-1;
return ans;
}
int main()
{
fread(ibuf,1,1<<23,stdin),n=read(),s=read();
for(int i=1;i<=n;++i) {i64 x=read();s1[i]=s1[i-1]+x,s2[i]=s2[i-1]+i*x;}
q=read();
for(int i=1,l,r;i<=q;++i) l=read(),r=read(),update(1,1,n,l,mid,0,i),update(1,1,n,mid+1,r,1,i);
for(int i=1;i<=n;++i) bound[i]=find(i);
solve(1,1,n);
for(int i=1;i<=q;++i) printf("%lld\n",ans[i]);
}