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;
}
posted @ 2018-10-11 11:24  Grary  阅读(248)  评论(1编辑  收藏  举报
博客园 首页 私信博主 编辑 关注 管理 新世界