P7962 方差 Sol

P7962 方差:DP 好题。

首先可以观察到,将 ai 赋为 ai1+ai+1ai 等同于交换 ai,ai+1 的差分数组。

下面令差分数组 di=ai+1ai

那么每次操作相当于交换 di1,di,不妨推广一下,题目等同于重排 d 后求最小方差。

首先求方差的式子可以转化为 ni=1nai2(i=1nai)2

也就是说我们需要通过方差数组求出全局和、平方和。

由于这两个数不太好同时转移,所以考虑设定一维成为状态。

接着考虑策略。考虑到方差本质上是反映一组数的波动情况,那么在差不变的情况下,显然要使得尽量多的数往中间靠。

那么想到对于 d 小的,可以尽量放在中间,使得差尽量小,更多的数靠近平均数,随后向左、向右严格非减。

所以做法是把 d 直接重排,每一次考虑放在原数组的左边 / 右边,然后计算 Δ,即改变的贡献。

式子就很好推了。

令当前状态 f(i,s) 表示放了 i1 个方差,原数组总和为 s

放左边的情况其实就是往右边所有放好的数加上一个差分 di

为什么是对的?方差和数本身无关,而与相对差有关。

在相对差不变的情况下,我们可以任意钦定 a1,这里默认 a10,则右边所有数 +di

放左边:f(i+1,s+i×di)=max{f(i,s)+j=1i(2ajdi+di2)}

化简得到 f(i+1,s+i×di)=max{f(i,s)+s×di+i×di2}

如果放右边,就不会对右边的数产生贡献,那么原数显然为 j=1idj

放右边:f(i+1,s+j=1idj)=max{f(i,s)+(j=1idj)2}

这里的 d 可以通过预处理得到。

那么这样枚举和的数量就很大了。

考虑到值域很小,所以有很多差分为 0,可以忽略贡献。

所以复杂度可以大大优化,对于 d 不同的转移即可。

最后输出 max{f(n,i)i2} 即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;

template <typename T> inline void read(T &x) {
  x = 0; char ch = getchar();
  while (!isdigit(ch)) ch = getchar();
  while (isdigit(ch)) x = (x<<1) + (x<<3) + (ch^48), ch = getchar();
}

const int N = 1e4 + 10;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, res = inf, a[N], cf[N], sum[N], dp[2][N<<6];
inline void Min(int &x, int y) { x = min(x, y); }

signed main() {
  read(n);
  for (int i = 1; i <= n; ++i)
    read(a[i]), cf[i] = a[i] - a[i - 1];
  cf[1] = inf; sort(cf + 1, cf + 1 + n);
  int num = upper_bound(cf + 1, cf + n, 0) - cf - 1;
  for (int i = 1; i < n; ++i) sum[i] = sum[i - 1] + cf[i];
  memset(dp[num & 1], 0x3f, sizeof dp[num & 1]);
  dp[num & 1][0] = 0;
  for (int i = num + 1; i < n; ++i) {
    memset(dp[i & 1], 0x3f, sizeof dp[i & 1]);
    for (int j = 0; j <= a[n] * n; ++j) {
      if (dp[1 - (i & 1)][j] == inf) continue;
      Min(dp[i & 1][j + sum[i]], dp[1 - (i & 1)][j] + sum[i] * sum[i]);
      Min(dp[i & 1][j + i * cf[i]], dp[1 - (i & 1)][j] + cf[i] * (cf[i] * i + 2 * j));
    }
  }
  for (int i = 0; i <= a[n] * n; ++i) {
    if (dp[1 - (n & 1)][i] == inf) continue;
    res = min(res, dp[1 - (n & 1)][i] * n - i * i);
  }
  cout << res << endl;
  return 0;
}
posted @   MistZero  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2021-10-20 Test on 2021/10/20
2021-10-20 Test on 2021/10/18
点击右上角即可分享
微信分享提示