YACS 2023年1月月赛 甲组 T2 分割数列(二) 题解

题目链接

继上个月的分割数列(一)又出了这道题。

首先还是考虑 $n^2DP$ ,设 $f[i]$ 为分到 $i$ 个的最小权重之和。

转移枚举上一个在哪里分就行了。

显然时间会超限,我们考虑一下优化:

首先,如果有连续的数字相同,那么就把他们放到一个块里。

处理完后还是 $n^2DP$,这样时间复杂度就减少到了 $O($块数$^2)$。

接着我们再来考虑剪枝,显然把前面每一个块划分成一个数列是比较优秀的。

他的代价为前面块的数量,所以我们转移的时候,当前的 $f[i]$ 已经超过了块数

那就没必要继续下去了,直接 $break$ 掉,平均时间复杂度 $O(n\sqrt{n})$。

不过比如一些毒瘤数据:两个数交替出现。就会卡成 $O(n^2)$,所以可以再加一个 $set$ 优化。

那样时间复杂度就成了铁 $O(n\sqrt{n}\times log_n)$ 就很难过,所以我就这样吧。

$T3$ 还是状压改天在写。

贴代码:

#include <iostream>
#include <cstring>
#define int long long
using namespace std;
int n, cnt, cnt1, tot;
int a[50005], f[50005], v[50005], tmp[50005], A[50005];
signed main() {
    scanf ("%d", &n);
    for (int i = 1; i <= n; i ++) {
        scanf ("%d", &a[i]);
        if (a[i] != a[i - 1]) A[++ tot] = a[i];
    }
    f[0] = 0;
    for (int i = 1; i <= tot; i ++) {
        tmp[A[i] ] ++;
        if (tmp[A[i] ] == 1) cnt1 ++;
        f[i] = i;
        memset (v, 0, sizeof v);
        v[A[i]] = 1;
        int cnt = 1;
        for (int j = i; j >= 1; j --)
        {
            if (cnt * cnt > i) break;
            if (!v[A[j] ]) {
                cnt ++;
                v[A[j]] = 1;
            }     
            f[i] = min (f[i], f[j - 1] + cnt * cnt);
        }
    }
    printf ("%d", f[tot]);
}

 

posted @ 2023-02-12 00:03  Xy_top  阅读(49)  评论(0)    收藏  举报