gym 102586 B Evacuation
首先先考虑\(O(NQ)\)的做法:
可以发现最坏情况一定是放在同一点,然后在此基础上,最终分布的区间两边长度的绝对值不能超过1 。
这个可以\(O(NlogN)\)预处理每一个点,询问\([1,n]\)的答案,这样查询任意\(x,l,r\)可以做到\(O(1)\)。
下面的优化就比较神仙了,需要发现当\(x\leq\frac{l+r}{2}\)时,只和\(l\)有关,然后随着\(l\)递增,最优的\(x\)也是递增的。
然后利用决策单调性,分治+线段树分治进行优化,时间复杂度为\(O(Nlog^2N)\)。
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define rep(a,b) for(int a=0;a<b;++a)
#define LL long long
#define PB push_back
#define POB pop_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=2e5+5;
int n;
LL s;
LL a[MAXN];
LL prefix[MAXN];
LL prefix2[MAXN];
LL ans[MAXN];
const int N=1<<18;
vector<pair<int,int > > seg[N+N];
void addi(int a,int b,mp val,int now=1,int l=1,int r=N+1){
if(r<=a||l>=b) return ;
if(r<=b&&l>=a) {
seg[now].PB(val);
return ;
}
int mid=(l+r)>>1;
addi(a,b,val,now<<1,l,mid);
addi(a,b,val,now<<1|1,mid,r);
}
int save[MAXN];
int init(int x){
int L=0,R=min(x-1,n-x)+1;
while(L<R-1){
int mid=(L+R)>>1;
mp lg,rg;
lg=II(x-mid,x);
rg=II(x+1,x+mid);
LL tot=0;
if(lg.FIR<=lg.SEC) tot+=prefix[lg.SEC]-prefix[lg.FIR-1];
if(rg.FIR<=rg.SEC) tot+=prefix[rg.SEC]-prefix[rg.FIR-1];
if(tot>s) R=mid;
else L=mid;
}
return L;
}
LL query(int x,int l,int r){
int L=min(save[x],min(x-l,r-x));
mp lg,rg;
lg=II(x-L,x);
check_max(lg.FIR,1);
rg=II(x+1,x+L);
check_min(rg.SEC,n);
LL lp,rp;
lp=rp=0;
if(lg.FIR<=lg.SEC) lp=prefix[lg.SEC]-prefix[lg.FIR-1];
if(rg.FIR<=rg.SEC) rp=prefix[rg.SEC]-prefix[rg.FIR-1];
LL rst=s-lp-rp;
LL dis=0;
dis=lp*x-rp*x+rst*(L+1);
if(lp) dis-=prefix2[lg.SEC]-prefix2[lg.FIR-1];
if(rp) dis+=prefix2[rg.SEC]-prefix2[rg.FIR-1];
return dis;
}
void dvdL(vector<mp> v,int l,int r){
if(v.empty()) return ;
int mid=v.size()/2;
vector<mp> L,R;
rep(i,mid) L.PB(v[i]);
rb(i,mid+1,v.size()-1) R.PB(v[i]);
int p=-1;
LL val=-1e18;
auto now=v[mid];
rb(i,l,r){
LL tmp=query(i,now.FIR,n);
if(tmp>val){
tie(val,p)=II(tmp,i);
}
}
check_max(ans[now.SEC],val);
dvdL(L,l,p);
dvdL(R,p,r);
}
void dvdR(vector<mp> v,int l,int r){
if(v.empty()) return ;
int mid=v.size()/2;
vector<mp> L,R;
rep(i,mid) L.PB(v[i]);
rb(i,mid+1,v.size()-1) R.PB(v[i]);
int p=-1;
LL val=-1e18;
auto now=v[mid];
rb(i,l,r){
LL tmp=query(i,1,now.FIR);
if(tmp>val){
tie(val,p)=II(tmp,i);
}
}
check_max(ans[now.SEC],val);
dvdR(L,l,p);
dvdR(R,p,r);
}
void run(int now=1,int l=1,int r=N+1){
vector<mp> L,R;
for(auto it:seg[now]){
if(it.SEC<0){
R.PB(II(it.FIR,-it.SEC));
}
else{
L.PB(it);
}
}
sort(ALL(L));
sort(ALL(R));
dvdL(L,l,r-1);
dvdR(R,l,r-1);
if(l==r-1) return ;
int mid=(l+r)>>1;
run(now<<1,l,mid);
run(now<<1|1,mid,r);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>s;
rb(i,1,n) cin>>a[i];
rb(i,1,n) prefix[i]=prefix[i-1]+a[i],prefix2[i]=prefix2[i-1]+a[i]*i;
rb(i,1,n) save[i]=init(i);
int q;
cin>>q;
rb(i,1,q){
int l,r;
cin>>l>>r;
int mid=(l+r)>>1;
addi(l,mid+1,II(l,i));
if(mid!=r)
addi(mid+1,r+1,II(r,-i));
}
rb(i,1,q) ans[i]=-1e18;
run();
rb(i,1,q) cout<<ans[i]<<endl;
return 0;
}