題意
U2FsdGVkX1+iGGcgnVGDR50qrY2/famzvdIJPHcDzBVzZJhRLBp940h3Ko01EEzw
dCJe5/clYmT54g7H2/jN11PfwmzHsyfCtE/EnJ3119dqExNBpE5hpA/UOudhV1AB
Wp4YKeE+LcFn4cAoEiDzxtuecW/dqGc6NXI8s/PKm7Q8rImznQ8UkFe41HlHylAr
hvTmL1KvuLMAkpNy1eXas90kpOW2lM5YYqixSjHkvgTzvjSFl+jVIQD2C3H5Dc/E
W3YrJqB1IWjlS5UlfQZeQ4QhZwtFPETJKxz1qysPhO7i1Iw19dT3GQUnZR6g08xL
ac3fuBQKqrLua+XZP6OHa3naTH/qtrGtsUTLoQOjqZb5qxLFftV2Jj8cnFLRf31S
F24QjiX+pb733NDW76dtIwccekiuxy9tkjVC27EsucP31HV2LO/RnLYXGjGOb2EA
WIx6UNrcQXdplDorxJ0GXm9w4p0dX+/66EJBYXghFA4nZiojXMheuon87/Bvqcsq
wK8it8lO4qMRjc3lmEXy1Y/AT4QcE28IuS44HC0tGNJhebTM8PEdFEgHKAlGOLPS
AFFrC5LIUrDsfj5kM/F+LaUSYozdUnqkTKC8gxOtUfx59A2z4YCJ4DN44v+m+8Ce
UE4hsJUF2vLaRUJzyAtgJjxCq/gKb4uLPNG0TBNDFVKNfZ0n5SM08pMVdHhkkLNd
2n7Ay589OUqqSNoZ0z6knOl26lyDCkdYGwarY+bZ67NUknajcOXDUzNkpFHOq2xV
gDsWjFTpbvDIMnLQfj04ZcH7D6/HPOKd2NdJoKFXA+8ov7iHTuDOnASMu3K0RcAt
dC18WYSrCXIj3r1UhZO+MTM2qG987vPuzxvHy0JKRXEq0Bk7t6/C3UrMQfFZcdJM
Z1JiDqEIde9wcY75J+sDGgthFMrnxIBn2ahZpo3eM7Y=
解析
先忽略掉第 次的額外代價。對於任意一對 ,如果他們在某次操作中產生了貢獻,那麼他們一定是本來在個連續端中,後來被分開了,所以任意一對 只會產生一次貢獻。同時經過 次操作之後, 被分成 段,即任意一對 不可能在同一段,也就是每一對 已經做過了貢獻。因此,分割的方法、順序不能影響最終的貢獻,且最終的貢獻就是:
接下來考慮第 次的額外代價。問題轉化為將長度為 的序列分割成 段,最小化上面的代價式子。設 表示分成 段,考慮前 個元素的最小代價,容易得到轉移方程:
來分析一下 的性質。對於 ,可以寫成:
設 。比較 和 的大小關係。考慮每一對 滿足 的貢獻:
- 如果 ,那麼兩個式子都要多上 的貢獻。
- 如果 , 中有且僅有一個在 中,兩個式子都多上 的貢獻。
- 如果 , 中沒有在 中的,則前者沒有貢獻,後者多 的貢獻。
綜上,有 ,即 滿足四邊形不等式。然後注意到 顯然滿足四邊形恆等式,所以 滿足四邊形不等式, 滿足決策單調性,可以分治計算。
最後,如何快速計算 ?可以用類似莫隊的思想,容易發現,左右端點的移動次數是 級別的。
實現
constexpr int sqr(int x) { return x * x; }
int main()
{
set_io("on");
int n, k, h;
std::cin >> n >> k >> h;
std::vector<int> a(n), cc(n), cca(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
cc[i] = a[i];
}
std::sort(cc.begin(), cc.end());
cc.erase(std::unique(cc.begin(), cc.end()), cc.end());
int m = cc.size();
for (int i = 0; i < n; i++) {
cca[i] = std::lower_bound(cc.begin(), cc.end(), a[i]) - cc.begin();
}
long long ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
ans += a[i] * a[j];
}
}
if (h == 0) {
std::cout << ans << std::endl;
return 0;
}
auto get_cost = [&a, &cca, m](int l, int r)
{
static long long q = 0;
static long long sum = 0;
static int ll = 0, rr = 0;
static std::vector<int> exist(m);
if (ll == rr) ll = rr = l;
while (rr < r) {
if (rr - ll >= 1) sum += std::abs(a[rr] - a[rr - 1]) + (a[rr] ^ a[rr - 1]);
q -= sqr(exist[cca[rr]]);
exist[cca[rr]]++;
q += sqr(exist[cca[rr]]);
rr++;
}
while (l < ll) {
ll--;
if (rr - ll >= 2) sum += std::abs(a[ll] - a[ll + 1]) + (a[ll] ^ a[ll + 1]);
q -= sqr(exist[cca[ll]]);
exist[cca[ll]]++;
q += sqr(exist[cca[ll]]);
}
while (r < rr) {
rr--;
if (rr - ll >= 1) sum -= std::abs(a[rr] - a[rr - 1]) + (a[rr] ^ a[rr - 1]);
q -= sqr(exist[cca[rr]]);
exist[cca[rr]]--;
q += sqr(exist[cca[rr]]);
}
while (ll < l) {
if (rr - ll >= 2) sum -= std::abs(a[ll] - a[ll + 1]) + (a[ll] ^ a[ll + 1]);
q -= sqr(exist[cca[ll]]);
exist[cca[ll]]--;
q += sqr(exist[cca[ll]]);
ll++;
}
return q * sum;
};
std::vector<long long> f(n + 1, std::numeric_limits<long long>::max() / 2);
std::vector<long long> g(n + 1);
f[0] = 0;
for (int i = 0; i < k; i++) {
std::fill(g.begin(), g.end(), std::numeric_limits<long long>::max() / 2);
auto solve = [&f, &g, &get_cost](auto &&self, int l, int r, int pl, int pr) -> void
{
int mid = l + (r - l) / 2;
int p = l;
for (int i = pl; i < mid && i <= pr; i++) {
long long w = f[i] + get_cost(i, mid);
if (g[mid] > w) {
p = i;
g[mid] = w;
}
}
if (l < mid) self(self, l, mid - 1, pl, p);
if (mid < r) self(self, mid + 1, r, p, pr);
};
solve(solve, 0, n, 0, n);
std::swap(f, g);
}
std::cout << ans + f[n] << std::endl;
}