peiwenjun's blog 没有知识的荒原

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

题目描述

给定下标从 \(1\) 开始,长为 \(n\) 的序列 \(a\) ,规定 \(a_0=\infty\)

\(\forall 1\le k\le n\) ,你需要构造序列 \(0=a_0\lt a_1\lt\cdots\lt a_m=k+1\) ,使得 \(\forall 0\le i\lt m-1\) ,有:

\[\sum_{a_i\le j\lt a_{i+1}}a_j\gt\sum_{a_{i+1}\le j\lt a_{i+2}}a_j\\ \]

并且序列 \(a_m,a_{m-1},\cdots,a_1\)字典序尽可能大(注意不要求最大化 \(m\)),输出 \(\sum_{i=1}^mi\cdot a_i\) 的值。

数据范围

  • \(1\le n\le 10^5,1\le a_i\le n\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{1GB}\)

分析

固定 \(k\) ,有一个显然的贪心,从后往前扫描序列 \(a\) ,如果当前段的和大于上一段,那么当前段到此结束,统计答案是容易的。

时间复杂度 \(\mathcal O(n^2)\) ,期望得分 \(60pts\)

还可以加一点乱搞优化,如果枚举不超过 \(5\) 个数,那就直接枚举,否则使用二分查找,理论最坏时间复杂度 \(\mathcal O(n^2\log n)\) ,但对随机数据优化效果非常明显,期望得分 \(85pts\)

正解是小清新分块。

\(\texttt{Key observation 1}\) :后续分段点仅与当前段 \([l,r]\) 有关。换言之可以记忆化,如果枚举到已经出现的 \([l,r]\) ,那么后续分段点的贡献可以 \(\mathcal O(1)\) 统计。

\(\texttt{Key observation 2}\) :被枚举到的本质不同区间 \([l,r]\) 仅有 \(\mathcal O(n\sqrt n)\) 个。

证明:

如果 \(r-l\le\sqrt n\) ,这样的区间只有 \(\mathcal O(n\sqrt n)\) 个。

如果 \(r-l\gt\sqrt n\) ,由于扫描的序列长度总和 \(\sum_{k=1}^nk=\mathcal O(n^2)\) ,所以区间个数不超过 \(\mathcal O(n\sqrt n)\)

代码实现时,可以仅对长度 \(\le\sqrt n\) 的区间记忆化,这样可以直接用二维数组(第二维把 \(r\) 改成 \(r-l\) )而不是哈希表存储。

由于需要二分查找分段点,时间复杂度 \(\mathcal O(n\sqrt n\log n)\)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,ll>
using namespace std;
const int B=200,maxn=1e5+5;
int n,top;
ll a[maxn],st[maxn];
pii h[maxn][B+5];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]+=a[i-1];
    for(int i=1;i<=n;i++)
    {
        ll j=0,res=0;
        st[top=0]=i+1;
        for(int l=i+1,r=i+1;l;)
        {
            int k=lower_bound(a,a+l-1,2*a[l-1]-a[r-1])-a;///sum [k,l)>sum [l,r)
            st[++top]=k,r=l,l=k;
            if(r-l<=B&&h[l][r-l].fi)
            {
                j=h[l][r-l].fi,res=h[l][r-l].se;
                break;
            }
        }
        for(int k=top;k>=0;k--,j++)
        {
            if(k&&st[k-1]-st[k]<=B) h[st[k]][st[k-1]-st[k]]=mp(j,res);
            res+=j*st[k];
        }
        printf("%lld%c",res," \n"[i==n]);
    }
    return 0;
}

posted on 2025-03-02 16:52  peiwenjun  阅读(76)  评论(0)    收藏  举报

导航