1 条题解
-
0
划分 题解
题目分析
这道题要求将数组划分为若干段,使得每段的和递增,并且最小化各段和的平方和。
关键约束:
- 段内元素连续
- 段和必须单调非递减
- 目标是最小化各段和的平方和
解题思路
核心观察
- 贪心性质:为了让平方和最小,应该让段尽可能均匀
- 单调队列:使用单调队列优化动态规划
- 决策单调性:最优决策点具有单调性
算法设计
代码采用了单调队列优化DP的方法:
主要步骤:
- 前缀和预处理:计算前缀和数组
- 单调队列维护:维护可能的最优决策点
- 二分查找:快速找到最优决策点
- 高精度计算:使用__int128存储结果
代码详解
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 40000005; int n, t, tl, tr; int a[N]; // 前缀和数组 signed la[N], stk[N]; // la: 决策点,stk: 单调队列 namespace IN { int x, y, z, b1, b2, id, p, l, r; // 初始化生成器参数 inline void pre() { cin >> x >> y >> z >> b1 >> b2 >> p >> p >> l >> r; } // 生成下一个a_i inline int gt() { if (!t) { // type=0,直接输入 cin >> x; return x; } // type=1,使用生成器 if (++id > p) cin >> p >> l >> r; int c = b1 % (r - l + 1) + l; int b3 = (b1 * y + b2 * x + z) & ((1 << 30) - 1); b1 = b2; b2 = b3; return c; } } // 输出__int128 void pr(__int128 x) { if (x >= 10) pr(x / 10); cout << (char)(x % 10 + '0'); } // 计算斜率:2*a[x] - a[la[x]] inline int gt(int x) { return a[x] * 2 - a[la[x]]; } // 二分查找最优决策点 inline int gtl(int x) { int l = tl, r = tr - 1; while (l < r) { int m = (l + r + 1) >> 1; if (gt(stk[m]) <= x) l = m; else r = m - 1; } return stk[l]; } void run() { cin >> n >> t; if (t) IN::pre(); // 初始化生成器 tr = 1; // 队列尾指针 int x = 0; // 当前前缀和 // 动态规划过程 for (int i = 1; i <= n; i++) { a[i] = (x += IN::gt()); // 计算前缀和 // 维护队列头部:删除不满足条件的决策点 while (tl + 1 < tr && gt(stk[tl + 1]) <= x) tl++; // 找到当前最优决策点 int c = la[i] = gtl(x); int y = x * 2 - a[c]; // 计算斜率 // 维护队列尾部:保持单调性 while (tr > tl && gt(stk[tr - 1]) >= y) tr--; stk[tr++] = i; // 加入新决策点 } // 计算最终答案 __int128 ans = 0; x = n; while (x) { int y = la[x]; ans += ((__int128)a[x] - a[y]) * (a[x] - a[y]); x = y; } pr(ans); } signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T = 1; while (T--) run(); return 0; }算法原理详解
1. 问题建模
设
a[i]为前缀和,f[i]为前i个元素的最小平方和,la[i]记录最优决策点。状态转移:
f[i] = min_{j} { f[j] + (a[i]-a[j])² } 约束条件:a[i]-a[j] ≥ a[j]-a[la[j]]2. 斜率优化
将约束条件改写为:
a[i] ≥ 2a[j] - a[la[j]]定义斜率
g(j) = 2a[j] - a[la[j]],则决策点j对i有效当且仅当g(j) ≤ a[i]。3. 单调队列维护
- 队列头部:删除
g(stk[tl+1]) ≤ a[i]的点,因为它们对后续决策不再重要 - 队列尾部:维护
g(stk)的单调递增性,保证决策最优性
4. 二分查找
使用二分在单调队列中找到满足
g(j) ≤ a[i]的最大j,即为当前最优决策点。复杂度分析
- 时间复杂度:O(n),每个元素入队出队一次
- 空间复杂度:O(n),存储前缀和和队列
样例解析
样例1:
5 0 5 1 7 9 9前缀和:5, 6, 13, 22, 31
最优划分:
- 段1: 5+1=6
- 段2: 7
- 段3: 9
- 段4: 9
平方和:6² + 7² + 9² + 9² = 36 + 49 + 81 + 81 = 247
关键技巧
- 斜率优化:将约束条件转化为斜率比较
- 单调队列:高效维护可能的最优决策点
- 二分查找:快速定位最优决策
- 高精度处理:使用__int128避免溢出
- 数据生成优化:支持大规模数据输入
总结
这道题的解题亮点:
- 问题转化:将划分问题转化为斜率优化DP
- 高效算法:O(n)时间解决大规模问题
- 单调性利用:充分利用决策单调性优化
- 工程实现:处理了大数据生成和高精度计算
算法通过深入的数学分析和巧妙的数据结构设计,在O(n)时间内解决了这个复杂的优化问题,展示了处理大规模动态规划问题的高级技巧。
- 1
信息
- ID
- 5039
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者