SP15648 APIO10A - Commando 题解
题目传送门
题目描述
给定一个长度为
思路:
这种序列问题的状态通常是一维的,所以我们定义
对于每个
对于一维线性 dp 问题,最好把它拿到一条直线上来看。
从图中可以看出,
综上所述,状态转移方程为:
其中
对于单组数据时间复杂度为
去掉
发现方程中含有
将只含
这就构成了斜率优化的基本形式,令
首先发现斜率
- 由于二次函数的
,所以斜率 单调递减; - 随着
的增大, 也单调递增,这意味着我们只会在右侧加点。
以上两个性质很重要,它们直接决定了维护方式!
这道题要我们求最大值,所以要使
当我们找最优决策即枚举
这就要求我们维护一个上凸壳。
外层循环每增加
这启示我们可以用单调队列来维护凸壳。
如图所示,当直线越来越倾斜时,就应将
根据图像不难看出,当队头与它右边那个点组成的直线斜率大于当前直线斜率时,就应该弹掉,如图所示:
维护凸壳也是同理,当队尾与它左边那个点组成的直线斜率小于新决策点与队尾组成直线的斜率时,就应该弹掉,如图所示:
具体步骤如下:
建立一个单调队列
- 排除无用决策,检查队头元素
和 构成的直线的斜率是否大于等于当前斜率,具体地,当
时,就应该将队头弹出;
-
取出队头
为最优决策,用它来计算 ; -
即将要把
插入队尾,检查队尾 、 和要加入的 是否构成一个上凸壳,具体地,当
时,就应该将队尾弹出。
其中
时间复杂度为
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1000010;
typedef long long ll;
int T;
int n;
ll a, b, c;
ll sum[N];
ll dp[N];
int q[N], hh, tt = -1;
inline ll sq(ll x) {return x * x;}
inline ll f(int x) {return a * sq(x) + b * x + c;}
inline ll Y(int x) {return dp[x] + a * sq(sum[x]);}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%lld%lld%lld", &n, &a, &b, &c);
for(int i = 1; i <= n; i++) {
scanf("%lld", &sum[i]);
sum[i] += sum[i - 1];
}
hh = tt = 0;
q[0] = 0; //首先加入的决策是 0
for(int i = 1; i <= n; i++) {
while(hh < tt && (Y(q[hh + 1]) - Y(q[hh])) >= (2 * a * sum[i] + b) * (sum[q[hh + 1]] - sum[q[hh]])) hh++; //弹掉过时决策
int j = q[hh];
dp[i] = dp[j] + f(sum[i] - sum[j]);
while(hh < tt && (Y(q[tt]) - Y(q[tt - 1])) * (sum[i] - sum[q[tt]]) <= (Y(i) - Y(q[tt])) * (sum[q[tt]] - sum[q[tt - 1]])) tt--; //维护上凸壳
q[++tt] = i;
}
printf("%lld\n", dp[n]);
memset(sum, 0, sizeof sum);
memset(dp, 0, sizeof dp);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!