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\) ,有:
并且序列 \(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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/18746428
浙公网安备 33010602011771号