1 条题解

  • 0
    @ 2026-4-25 13:49:19

    不错的题。研究环状序列的分割,使用经典套路:把序列复制一份接在后面,对新序列的每个长为 nn 的区间做。很容易能说明所有分段方案都必然存在一个在新序列上的映射。

    方便刻画区间和,先把序列前缀和,让 sis_i 表示 [1,i][1,i] 的和。

    我们要把序列分成三段 [i,j],[j+1,k],[k+1,i+n1][i,j],[j+1,k],[k+1,i+n-1],记 R=i+n1R=i+n-1。从小到大枚举 ii,并考虑钦定一个这些段内和之间的大小关系。从左到右依次标号这些段,假设我们是要 g1g2g3g_1\geq g_2\geq g_3

    于是我们有如下限制(简洁起见,跳了一步列式):

    $$\left\{ \begin{aligned} \frac{s_{i-1}+s_k}{2}\leq &s_{j}\\ \frac{s_{i+n}+s_j}{2}\leq &s_k \end{aligned} \right.$$

    理一个关于 sks_k 的式子,关注 sjs_jsks_k 的关系:

    si+n+sj2sk2sjsi1\frac{s_{i+n}+s_j}{2}\leq s_k\leq 2s_j-s_{i-1}

    而我们要最小化:

    (sjsi1)(si+nsk)(s_j-s_{i-1})-(s_{i+n}-s_k)

    其中 si1+si+ns_{i-1}+s_{i+n} 已经确定,我们要最小化的就是 sj+sks_j+s_k。由于 aia_i 非负,所以 sis_i 单增,我们要让 j,kj,k 尽可能靠前。不难发现这个限制是统一的,即 i,j,ki,j,k 三个指针在过程中的移动是统一的(单增):

    假设我们暴力从小到大枚举 jj,此时 sks_k 的下界不断提升,则可能的 kk 在不断向后,当刚好存在在限制区间内的 sks_k 的时候,这个最小的 kk 就是我们要找的。加入对 ii 的考虑:在 ii 不断变大的过程中,对于相同的 jj,对 sks_k 的限制下界变大,上界变小,则更难以存在合法的 kk

    于是我们就说明了三个指针移动方向的统一性。

    最后,事实是,我们只需要钦定两次这样的大小关系,在环状序列上划分三段的大小关系本质只有两种:g1g2g3g_1\geq g_2\geq g_3 以及 g1g2g3g_1\leq g_2\leq g_3。所以,你可以直接把序列 reverse 再做一遍,即可。时间复杂度 O(n)O(n)

    在感冒,代码里的细节理了一会儿,不过不算难写。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    const int N=2e6+10;
    const LL INF=1e18;
    int n,a[N];
    LL s[N];
    array<LL,4> ans={INF,0,0,0};
    inline LL rsum(int i,int j){
        return s[j]-s[i-1];
    }
    void solve(int rev){    
        for(int i=1;i<=n*2;i++)
            s[i]=s[i-1]+a[i];
        auto turn=[&](LL x){
            if(rev) x=n*2-(x-1)+1;    // 从区间的另一侧起点做——也就是自己先减 1 再倒过来。
            return x%n==0?n:x%n;
        };
        for(int i=1,j=0,k=0,R=n;i<=n;i++,R++){
            j=max(j,i);
            while(j+1<R){
                for(k=max(j+1,k);k<R && rsum(j+1,k)<rsum(k+1,R);k++);
                if(k+1<=R && rsum(i,j)>=rsum(j+1,k) && rsum(j+1,k)>=rsum(k+1,R)){
                    ans=min(ans,{rsum(i,j)-rsum(k+1,R),turn(i),turn(j+1),turn(k+1)});
                    break ;
                }
                j++;
            }
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),a[i+n]=a[i];
    
        solve(0);
        reverse(a+1,a+1+2*n);
        solve(1);
    
        sort(ans.begin()+1,ans.end());
        printf("%lld\n%lld %lld %lld\n",ans[0],ans[1],ans[2],ans[3]);
        return 0;
    }
    
    • 1

    信息

    ID
    10190
    时间
    300ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者