【题解】P11822 [湖北省选模拟 2025] 团队分组 divide

P11822 [湖北省选模拟 2025] 团队分组 / divide

题意

有一个序列 \(v\),第 \(i\) 天会有 \(v_i\) 加入 \(v\) 的末尾,且 \(v_0=10^{10^{100}}\)

你需要将 \(v\) 分段。具体的,假设现在已经有 \(|v|=k\),请找到一个序列 \(0=a_0<a_1<\dots <a_m=k+1\),使得对于所有 \(0\le i<m-1\),有 \(\sum\limits_{j=a_i}^{a_{i+1}-1}v_j>\sum\limits_{j=a_{i+1}}^{a_{i+2}-1}v_j\)

要求构造出的 \(a_m\) 尽可能大,在此基础上 \(a_{m-1}\) 尽可能大,在此基础上 \(a_{m-2}\) 尽可能大,以此类推。即让序列 \(a_m,a_{m-1},a_{m-2}\dots a_1\) 的字典序尽可能大。

分别求出在 \(k=1,2\dots n\) 的时候, \(\sum\limits_{i=0}^m(i\times a_i)\) 的值。

题解

知识点:二分,记忆化,根号分治。

启发:

  • 考虑一定范围内的记忆化。

确实很有省选 D1T1 的风格。

考虑怎样才能使得分段方式最优,这是类似于最大化字典序的问题,不难得出一个简明的贪心算法:

让最后一个数自成一段,往前扫,只要扫过的和大于前一段的和就新开一段,朴素实现复杂度是 \(O(n)\)

题目要求对 \(v\) 每一个的前缀都跑一遍这样的算法,不过显然不能暴力地对每一段都跑一遍,这样是 \(O(n^2)\) 的。

上述贪心过程可以用二分快速找到前一个端点,可以用类似 sol(l,r) 来递归实现这个贪心过程。

注意到如果递归到了一个段 \((l,r)\),那么 \(l\) 之前的分段方式则可以完全确定,可以考虑记忆化 \((l,r)\) 时的答案。

\((l,r)\) 的规模是 \(O(n^2)\) 级别的,无脑记忆化会爆空间。

为了解决这个问题,不妨设置一个阈值 \(B\),对于 \(r-l+1\le B\) 的记忆化,对于 \(r-l+1>B\) 的暴力跳。

综合分析可知 \(B=\sqrt{n}\) 时达到理论最优复杂度(但实际上 \(B\)\(24\) 左右的时候最快,不知道是不是数据问题),\(r-l+1\le B\)\((l,r)\) 规模是 \(O(n\sqrt{n})\) 的,\(r-l+1>B\)\((l,r)\) 向前跳是复杂度 \(O(\sqrt{n})\) 的,每个 \(i\) 往前跳也最多 \(O(n\sqrt{n})\)

所以可以达到理论最优复杂度 \(O(n \sqrt{n} \log_2{n} )\)

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 102506
#define M 321
#define int long long

int n,len,a[N];
pr mem[N][M];

inline pr sol(int l,int r){
    if(r-l+1<=len&&mem[l][r].fi){
        return mem[l][r];
    }

    int id=lower_bound(a,a+l,2*a[l-1]-a[r-1])-a;

    pr res;

    if(id){
        res=sol(id,l);
    }
    else{
        res={0,0};
    }

    res.fi+=res.se*id;
    res.se++;

    if(r-l+1<=len){
        return mem[l][r]=res;
    }
    return res;
}

signed main(){
    // freopen("divide.in","r",stdin);
    // freopen("divide.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n;

    len=24;

    rep(i,1,n){
        cin>>a[i];
        a[i]+=a[i-1];
    }

    rep(i,1,n){
        pr res=sol(i,i+1);
        cout<<res.fi+(i+1)*(res.se+1)+i*res.se<<' ';
    }

    return 0;
}
posted @ 2025-06-29 00:48  Lucyna_Kushinada  阅读(16)  评论(0)    收藏  举报