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]);
}
posted @ 2020-04-29 09:43  Shiina_Mashiro  阅读(420)  评论(0编辑  收藏  举报