CF868F Yet Another Minimization Problem

题目描述:

给定一个序列,要把它分成k个子序列。每个子序列的费用是其中相同元素的对数。求所有子序列的费用之和的最小值。

输入格式:第一行输入n(序列长度)和k(需分子序列段数)。下一行有n个数,序列的每一个元素。

输出格式:输出一个数,费用和的最小值。

2<=n<=10^5,2<=k<=min(n,20),序列的每一个元素值大于等于1,小于等于n。

Solution

思路还是比较单纯
\[ f_{i,j}=f_{i-1,k}+g_{k+1,j} \]
有m次每次是\(O(n)\)的转移.

可以利用决策单调性转移.
方法是将区间从中间分开.
找到分界点的决策点.

这样原区间和决策区间都被一份为二.
于是递归处理
就是实在有点麻烦.

Code

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
const int N = 100005;
const int inf = 0x3f3f3f3f;
using std:: fill;
using std:: swap;
using std:: min;
using std:: max;

int A[N];
long long f[N];
long long g[N];
int B[N];

void solve(int l, int r, int L, int R, long long P) {
    if (l > r) return ;
    int m = l + r >> 1;
    int p = min(m, R);
    int M = 0;
    for (int i = l; i <= m; i += 1) P += B[A[i]], B[A[i]] += 1;
    for (int i = L; i <= p; i += 1) 
        P -= (B[A[i]] -= 1), g[i] + P < f[m] ? M = i, f[m] = g[i] + P : 0;
    for (int i = l; i <= m; i += 1) P -= (B[A[i]] -= 1);
    for (int i = L; i <= p; i += 1) P += B[A[i]], B[A[i]] += 1;
    solve(l, m - 1, L, M, P);
    for (int i = L; i <  M; i += 1) P -= (B[A[i]] -= 1);
    for (int i = l; i <= m; i += 1) P += B[A[i]], B[A[i]] += 1;
    solve(m + 1, r, M, R, P);
    for (int i = l; i <= m; i += 1) B[A[i]] -= 1;
    for (int i = L; i <  M; i += 1) B[A[i]] += 1;
}

int main () {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i += 1)
        scanf("%d", &A[i]);
    for (int i = 1; i <= n; i += 1)
        g[i] = g[i - 1] + B[A[i]], B[A[i]] += 1;
    memset(B, false, sizeof B);
    for (int i = 1; i <= m; i += 1) {
        memset(f, 0x3f, sizeof f);
        solve(1, n, 1, n, 0);
        swap(f, g);
    }
    printf("%lld\n", f[n]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qdscwyy/p/9771434.html