【题解】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;
}

浙公网安备 33010602011771号