专栏文章

题解:P11947 [KTSC 2025] 可爱区间 / maxsum

P11947题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mipslu5w
此快照首次捕获于
2025/12/03 17:16
3 个月前
此快照最后确认于
2025/12/03 17:16
3 个月前
查看原文
一个很简洁的做法。
先思考如何判断一个区间 [l,r][l,r] 是否合法。显然我们会给 [l,r][l,r] 内的下标全选 bb,其余下标全选 aa。条件无非三个:
  • [l,r][l,r] 任意缩一段前缀 / 后缀,不会因为这段前缀 / 后缀和 <0<0 而变大;
  • [l,r][l,r] 任意扩展一段前缀 / 后缀,不会因为这段前缀 / 后缀和 >0>0 而变大;
  • [1,l1][1,l-1][r+1,n][r+1,n] 内的 aa 最大子段和 b[l,r]\le b[l,r] 的和。
我们发现第三个条件可以直接考虑全局非空的 aa 最大子段和,因为 bb 的限制在中间那段只会更紧。
形式化地描述,我们需要满足:(设 ssbb 的前缀和数组)
  • i[l,r],srsl1sisl1\forall i \in [l,r], s_r-s_{l-1}\ge s_i-s_{l-1},即 srs_rs[l,r]s[l,r] 的最大值;
  • i[l,r],srsl1srsi1\forall i \in [l,r], s_r-s_{l-1}\ge s_{r}-s{i-1},即 sl1s_{l-1}s[l1,r1]s[l-1,r-1] 的最小值;
  • prel1=0pre_{l-1}=0,其中 preipre_i 表示 a[1,i]a[1,i] 的最大子段和;
  • sufr+1=0suf_{r+1}=0,其中 sufisuf_i 表示 a[i,n]a[i,n] 的最大子段和;
  • srsl1Ms_r-s_{l-1}\ge M,其中 MM 表示 aa 序列的最大非空子段和。
考虑扫描线,扫描 rr,维护所有合法的 ll
先观察第一个条件,容易发现这限制了 ll 必须在某个区间内,太靠左最大值就会超过 srs_r。设这个限制范围为 [limr,r][lim_r,r]
再考虑第二个条件,这个条件是关于 ll 的,一种处理方法是同样处理出这个区间,然后扫描的同时加入和删除对应的 ll。但是这样不太方便解决第五个限制。
更机智的处理方法是,观察到合法的 ll 在扫到 rr 时,sl1s_{l-1} 一定是后缀最小值。 因此我们可以维护一个单调栈,存所有合法的 ll,这样的好处是 srsl1Ms_r-s_{l-1}\ge M 满足条件的就一定是单调栈上的一段前缀了(因为这时候 sl1s_{l-1} 越前越小,限制更松)。利用这个单调性,我们就可以在查询的时候,在单调栈上二分出有效的区间。
第三、四个条件只和 l,rl,r 中一个相关,只加入合法的点即可。
因此我们扫描线的过程大致如下:
  • 计算所有以 rr 为右端点的合法区间:单调栈上二分,线段树查询;
  • 弹出所有 sl01>srs_{l_0-1}>s_{r}l0l_0,并将不合法的 l0l_0 从线段树上删除;
  • 加入 l1=rl-1=r 这个决策,并在线段树上更新。
最后还需要支持多次查询,询问差分一下之后是个单点修改区间历史和,类似维护即可。
时间复杂度 O((n+q)logn)\mathcal O((n+q)\log n)
一份自认为实现的很帅的代码,供大家参考(如果不理解也可以看看代码,手模几步过程应该很快就会懂的)
CPP
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
#define rep(i,a,b) for(ll i=(a);i<=(b);++i)
#define per(i,a,b) for(ll i=(a);i>=(b);--i)
const ll maxN=5e5+9;
ll tr[maxN<<2],his[maxN<<2],tag[maxN<<2],n,m,sumb[maxN],pre[maxN],suf[maxN],abst,lim[maxN];
void Pushdown(ll x){
    if(!tag[x])return ;
    his[x<<1]+=tr[x<<1]*tag[x];
    tag[x<<1]+=tag[x];
    his[x<<1|1]+=tr[x<<1|1]*tag[x];
    tag[x<<1|1]+=tag[x];
    tag[x]=0;
}
void Upd(ll x,ll l,ll r,ll u,ll v){
    if(l==r)return tr[x]+=v,void();
    ll mid=(l+r)>>1;
    Pushdown(x);
    if(u<=mid)Upd(x<<1,l,mid,u,v);
    else Upd(x<<1|1,mid+1,r,u,v);
    tr[x]=tr[x<<1]+tr[x<<1|1];
    his[x]=his[x<<1]+his[x<<1|1];
}
void Hist(ll x,ll l,ll r,ll ql,ll qr){
    if(ql<=l&&r<=qr)return his[x]+=tr[x],tag[x]++,void();
    ll mid=(l+r)>>1;
    Pushdown(x);
    if(ql<=mid)Hist(x<<1,l,mid,ql,qr);
    if(qr>mid)Hist(x<<1|1,mid+1,r,ql,qr);
    tr[x]=tr[x<<1]+tr[x<<1|1];
    his[x]=his[x<<1]+his[x<<1|1];
}
ll Query(ll x,ll l,ll r,ll ql,ll qr){
    if(ql>qr)return 0;
    if(ql<=l&&r<=qr)return his[x];
    Pushdown(x);
    ll mid=(l+r)>>1;
    if(qr<=mid)return Query(x<<1,l,mid,ql,qr);
    if(ql>mid)return Query(x<<1|1,mid+1,r,ql,qr);
    return Query(x<<1,l,mid,ql,qr)+Query(x<<1|1,mid+1,r,ql,qr);
}
vector<array<ll,2> >vq[maxN];
ll stk[maxN],top;
std::vector<long long> maxsum(
    std::vector<int> A, std::vector<int> B, 
    std::vector<int> L1, std::vector<int> R1, 
    std::vector<int> L2, std::vector<int> R2){
    n=A.size(),m=L1.size();
    A.insert(A.begin(),0),B.insert(B.begin(),0);
    vector<ll>ans(m,0);
    for(int&x:L1)x++;
    for(int&x:L2)x++;
    for(int&x:R1)x++;
    for(int&x:R2)x++;
    rep(i,0,m-1){
        vq[L2[i]-1].push_back({-1,i});
        vq[R2[i]].push_back({1,i});
    }
    rep(i,1,n)sumb[i]=sumb[i-1]+B[i];
    rep(i,1,n)pre[i]=max(pre[i-1]+A[i],0ll);
    per(i,n,1)suf[i]=max(suf[i+1]+A[i],0ll);
    abst=-1e18;
    rep(i,1,n)abst=max(abst,pre[i-1]+A[i]);
    rep(i,1,n){
        while(top&&sumb[stk[top]]<=sumb[i])top--;
        lim[i]=stk[top],stk[++top]=i;
    }
    memset(stk,0,sizeof(stk));
    top=1,Upd(1,0,n,0,1);
    rep(r,1,n){
        ll ql=1,qr=top,ok=0;
        while(ql<=qr){
            ll mid=(ql+qr)>>1;
            if(sumb[r]-sumb[stk[mid]]>=abst)ok=mid,ql=mid+1;
            else qr=mid-1;
        }
        if(suf[r+1]==0&&ok)Hist(1,0,n,lim[r],stk[ok]);
        for(array<ll,2> qu:vq[r]){
            ll sgn=qu[0],id=qu[1];
            ll lf=L1[id]-1,rg=R1[id]-1;
            ans[id]+=sgn*Query(1,0,n,lf,rg);
        }
        while(top&&sumb[stk[top]]>sumb[r]){
            if(pre[stk[top]]==0)Upd(1,0,n,stk[top],-1);
            top--;
        }
        if(pre[r]==0)Upd(1,0,n,r,1);
        stk[++top]=r;
    }
    return ans;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...