[luogu4072] 征途

题面

题意体面中写得很明确, 应该不用我说了, 方差的概念在初中人教版九年级数学中有所提到, 没有上过初中的同学们可以左转百度.

将序列拆为几段求最值, 我们考虑用dp来实现.

先推一下式子, 令方差为vv, rr为某一段路径的长度, ¯r¯¯¯r为这mm条路径长度的平均数.

则有:

v=mi=1(¯rri)2m=mi=1r2i+m(¯r)22(¯r)mi=1rim=mi=1r2i+m(mi=1rim)22(mi=1ri)2mm=(mi=1ri)2m2+mi=1r2imv=mi=1(¯¯¯rri)2m=mi=1r2i+m(¯¯¯r)22(¯¯¯r)mi=1rim=mi=1r2i+m(mi=1rim)22(mi=1ri)2mm=(mi=1ri)2m2+mi=1r2im

答案所求为vm2vm2, 所以:

vm2=mmi=1r2i(mi=1ri)2vm2=mmi=1r2i(mi=1ri)2

我们发现(mi=1ri)2(mi=1ri)2是一个定值, 也就是总路径长度的平方, 所以我们只需要求前面那个数就可以了, 用前缀和先处理一下, 即SiSi表示1 ~ i的路径的长度.

我们设f[i][k]f[i][k]表示前ii段路花kk天走的最小花费, 所以我们可以得到状态转移方程:

f[i][k]=min(f[i][k],f[j][k1]+(sum[j]sum[i])2)f[i][k]=min(f[i][k],f[j][k1]+(sum[j]sum[i])2)

还是老套路, 设对ii点选tt(tt > jj)转移比选jj转移更优, 则有:

f[t][k1]+(sum[t]sum[i])2<f[j][k1]+(sum[j]sum[i])2(f[t][k1]+sum[t]2)(f[j][k1]+sum[j]2)<2sum[i](sum[t]sum[j])(f[t][k1]+sum[t]2)(f[j][k1]+sum[j]2)2(sum[t]sum[j])<sum[i]f[t][k1]+(sum[t]sum[i])2<f[j][k1]+(sum[j]sum[i])2(f[t][k1]+sum[t]2)(f[j][k1]+sum[j]2)<2sum[i](sum[t]sum[j])(f[t][k1]+sum[t]2)(f[j][k1]+sum[j]2)2(sum[t]sum[j])<sum[i]

由此可知道我们需要维护一个下凸包, 不信的话选三个点试一下就可以发现了, 然后愉快的推式子时间又结束了, 下面是大家喜(shen)闻(wu)乐(tong)见(jue)的CodingTimeCodingTime...

代码

Copy
#include <iostream> #include <cstring> #include <cstdio> #define N 3005 using namespace std; int n, m, sum[N], q[N], l, r; long long f[N], g[N]; inline int read() { int x = 0, w = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return x * w; } bool F_check(int x, int y, int z) { return 1ll * (g[y] - g[x] + sum[y] * sum[y] - sum[x] * sum[x]) < 1ll * 2 * z * (sum[y] - sum[x]); } bool S_check(int x, int y, int z) { return (g[y] - g[x] + sum[y] * sum[y] - sum[x] * sum[x]) * (sum[z] - sum[y]) > (g[z] - g[y] + sum[z] * sum[z] - sum[y] * sum[y]) * (sum[y] - sum[x]); } int main() { n = read(); m = read(); for(int i = 1; i <= n; i++) { sum[i] = read(); sum[i] += sum[i - 1]; g[i] = 1ll * sum[i] * sum[i]; } for(int k = 1; k < m; k++) { l = 1; r = 0; q[++r] = k; for(int i = k + 1; i <= n; i++) { while(l < r && F_check(q[l], q[l + 1], sum[i])) l++; f[i] = g[q[l]] + 1ll * (sum[i] - sum[q[l]]) * (sum[i] - sum[q[l]]); while(l < r && S_check(q[r - 1], q[r], i)) r--; q[++r] = i; } for(int i = 1; i <= n; i++) g[i] = f[i]; } printf("%lld\n", m * f[n] - sum[n] * sum[n]); return 0; }

不知道交了多少遍才AC的菜鸡我

posted @   ztlztl  阅读(328)  评论(1编辑  收藏  举报
编辑推荐:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示