gym 102586 B Evacuation

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;
}
上一篇:c-跳到argv吗?


下一篇:让 WPF 的 RadioButton 支持再次点击取消选中的功能