CF 1582E. Pchelyonok and Segments
CF 1582E.Pchelyonok and Segments
题意
给出长度为 \(n\) 的序列 \(a\) ,你可以把 \(a\) 分成任意 \(k\) 组不相交的子数组,要求:
-
第一个子数组长度为 \(k\) ,第二个为 \(k-1\) ,\(...\) ,第 \(k\) 个长度为 \(1\) 。
-
记第 \(i\) 个子数组和为 \(sum_i\) ,要求 \(sum_1 \lt sum_2 \lt ... \lt sum_k\) 。
请问 \(k\) 最大为多少?
\(1 \le n \le 10^5, 1 \le a_i \le 10^9\) 。
分析
首先,根据每段的长度,子数组一共有 \(\dfrac{k * (k + 1)}{2} \le n\) 个,我们发现 \(k\) 是不超过 \(500\) 的,
我们设 \(f(pos, t)\) 表示从 \(pos\) 位置开始时,把 \(pos \sim n\) 分成 \(t\) 段时第一段子数组和的最大值(贪心,这样前面可选择的可能性增大) ,
由于最后一段长度 \(1\) 是确定的,而前面是不确定的,会根据 \(t\) 的值改变,所以我们从后往前转移。
求 \(sum_i\) 可以使用前缀和思想。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define per(i, x, y) for(int i = x; i >= y; i--)
#define forr(x, s) for (auto x : s)
#define all(a) begin(a),end(a)
#define lc(x) (x<<1)
#define rc(x) (x<<1|1)
#define pb emplace_back
#define int long long
using namespace std; using PII = pair<int, int>;
const int N = 100010, M = 6000, mod = 998244353, INF = 0x3f3f3f3f;
int a[N], post[N]; // a 为原数组,post 为后缀和
int dp[N][550]; // dp[pos][len] 表示从pos位置开始,可以产生len段的,最近一段的最大和
void solve ()
{
int n; cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = n; i >= 1; i -- ) post[i] = post[i + 1] + a[i];
for (int i = 1; i <= n; i ++ )
for (int j = 0; j <= 550; j ++ )
dp[i][j] = 0;
dp[n][1] = a[n];
int ans = 1;
for (int i = n - 1; i >= 1; i -- )
{
dp[i][1] = max(dp[i + 1][1], a[i]); // 转移以1为段的最大和
// 枚举大于两端的最大和
for (int j = 2; j <= 500 && i + j <= n; j ++ )
{
dp[i][j] = dp[i + 1][j]; // 转移
if (dp[i + j][j - 1] != 0 && post[i] - post[i + j] < dp[i + j][j - 1])
{
dp[i][j] = max(dp[i][j], post[i] - post[i + j]);
ans = max(ans, j);
}
}
}
cout << ans << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; )
solve();
return 0;
}