CodeForces 868F Yet Another Minimization Problem(决策单调性优化 + 分治)

Posted zjp-shadow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces 868F Yet Another Minimization Problem(决策单调性优化 + 分治)相关的知识,希望对你有一定的参考价值。

题意

给定一个序列 ({a_1, a_2, cdots, a_n}),要把它分成恰好 (k) 个连续子序列。

每个连续子序列的费用是其中相同元素的对数,求所有划分中的费用之和的最小值。

(2 le n le 10^5, 2 le k le min(n, 20), 1 le a_i le n)

题解

(k) 比较小,可以先考虑一个暴力 (dp)

(dp_{k, i}) 为前 (i) 个数划分成 (k) 段所需要的最小花费。

那么转移如下
[ dp_{k, i} = min_{j le i} dp_{k - 1, j - 1} + w_{j, i} ]

其中 (w_{j, i})(j sim i) 这段划分出来需要的花费,也就是 ([j, i]) 区间内相同元素对数。

暴力做是 (O(n^2 k)) 的,无法通过。

说到最优区间划分,我就想起了决策单调性,今年下半年

至于为什么满足决策单调?考虑证明 (mathrm{1D/1D}) 上的 四边形不等式。具体证明可以参考此处

我们现在只有一个问题了, 就是如何快速求出 (w_{j, i})

可以考虑把序列分块,然后预处理块到块的答案以及点到一个块的答案,然后再算算边角。

然后这个配合 二分+单调栈 可以做到 (O(nk sqrt n log n)) ,还是过不去。

对于这种分层 (dp) 来说,分治的复杂度就可以正确,因为每次不需要先分治左区间再算右区间,可以扫完整个区间得到 (mid) 的最优决策点,然后就可以把 ([l, mid))((mid, r]) 的决策点分开了。

这样单次求解的话,每层是 (O(n)) 的,那么复杂度是 (O(n log n)) 的。

然后此时我们就可以很好的算 (w_{j, i}) 了,要怎么算呢?

可以暴力一点做,考虑类似莫队那样维护当前计算区间的 ([l, r]) ,然后看接下来要算的 ([l', r']) 的相对位置,就可以得到相应的区间的花费了。

复杂度?其实是对的。具体原因可以参考非指针移动的那种做法,每次只会移动当前区间长度的指针。

这个其实是一样的,因为每次需要利用的相邻两个区间是一样的,这种移动方法的复杂度是平面上两点的曼哈顿距离,显然不会更劣。

那么最后复杂度就是 (O(nk log n)) 的,似乎我的写法跑的挺快?

代码

很好写啊qwq

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

typedef long long ll;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("F.in", "r", stdin);
    freopen ("F.out", "w", stdout);
#endif
}

const int N = 2e5 + 1e3;

int n, k, a[N], times[N];

int l, r; ll res, dp[25][N];

void Move(int L, int R) {
    while (l > L) res += times[a[-- l]] ++;
    while (l < L) res -= -- times[a[l ++]];
    while (r > R) res -= -- times[a[r --]];
    while (r < R) res += times[a[++ r]] ++;
}

void Divide(int k, int l, int r, int dl, int dr) {
    if (l > r) return;
    int mid = (l + r) >> 1, dmid = dl;
    dp[k][mid] = 0x3f3f3f3f3f3f3f3f;
    For (i, dl, min(mid, dr)) {
        Move(i, mid);
        if (chkmin(dp[k][mid], dp[k - 1][i - 1] + res)) dmid = i;
    }
    Divide(k, l, mid - 1, dl, dmid);
    Divide(k, mid + 1, r, dmid, dr);
}

int main () {

    File();

    n = read(); k = read();

    For (i, 1, n) a[i] = read();

    For (i, 1, n)
        dp[1][i] = (res += times[a[i]] ++);
    res = 0; Set(times, 0);

    l = 1; r = 0;
    For (i, 2, k) Divide(i, 1, n, 1, n);
    printf ("%lld
", dp[k][n]);

    return 0;

}

以上是关于CodeForces 868F Yet Another Minimization Problem(决策单调性优化 + 分治)的主要内容,如果未能解决你的问题,请参考以下文章

CF868F Yet Another Minimization Problem 题解

CF868F Yet Another Minimization Problem

[CF868F] Yet Another Minimization Problem

Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task

Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task

CodeForces - 393E Yet Another Number Sequence