CF868F Yet Another Minimization Problem 解题报告

题意翻译

题目描述:给定一个序列 a,要把它分成 k 个子段。每个子段的费用是其中相同元素的对数。求所有子段的费用之和的最小值。 输入格式:第一行输入 n(序列长度)和 k(需分子段数)。接下来一行有 n 个数,第 i 个数表示序列的第 i 个元素 ai。 输出格式:输出一个数,费用和的最小值。 2n1052kmin(n,20)1ain

题目描述

You are given an array of n integers a1... an . The cost of a subsegment is the number of unordered pairs of distinct indices within the subsegment that contain equal elements. Split the given array into k non-intersecting non-empty subsegments so that the sum of their costs is minimum possible. Each element should be present in exactly one subsegment.

输入输出格式

输入格式

The first line contains two integers n and k ( 2<=n<=105 , 2<=k<=min (n,20)) — the length of the array and the number of segments you need to split the array into. The next line contains n integers a1,a2,...,an ( 1<=ai<=n ) — the elements of the array.

输出格式

Print single integer: the minimum possible total cost of resulting subsegments.

输入输出样例

输入样例 #1

7 3
1 1 3 3 3 2 1

输出样例 #1

1

输入样例 #2

10 2
1 2 1 2 1 2 1 2 1 2

输出样例 #2

8

输入样例 #3

13 3
1 2 2 2 1 2 1 1 1 2 2 1 1

输出样例 #3

9

说明

In the first example it's optimal to split the sequence into the following three subsegments: [1] , [1,3] , [3,3,2,1] . The costs are 0 , 0 and 1 , thus the answer is 1 . In the second example it's optimal to split the sequence in two equal halves. The cost for each half is 4 . In the third example it's optimal to split the sequence in the following way: [1,2,2,2,1] , [2,1,1,1,2] , [2,1,1] . The costs are 4 , 4 , 1 .

SOLUTION

首先来一波BF~ 🤢

fi,j表示前i个数分成j段的最小费用

fi,j=mink<i(fk,j1+w(k+1,i))

emmm...很好,O(kn2),直接寄掉。

于是我们考虑优化

我们可以浅浅发现好像这个dp满足决策单调性

因为fi,j的最优决策点肯定小于等于fi+1,j

具体证明:(略)

于是我们就可以通过分治优化dp

接下来的问题就是如何求出w(k+1,i)

我们可以借助莫队的思想,挪动l,r端点来达到优化时间的目的

总复杂度O(knlogn)

CODE

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+2;
ll L=1,R,f[N][23],a[N],cnt[N],n,m,ans,k;
ll w(ll l, ll r){//动态算一下贡献,类似于莫队 
	while(L<l) ans-=(--cnt[a[L++]]);
	while(R>r) ans-=(--cnt[a[R--]]);
	while(L>l) ans+=cnt[a[--L]]++;
	while(R<r) ans+=cnt[a[++R]]++;
	return ans;
}
void solve(ll l,ll r,ll L,ll R){//分治 
	if(l>r) return;
	ll p=0,mid=(l+r)>>1;
	for(ll i=L; i<=min(R,mid-1); i++){//p:最优决策点
		if(f[i][k-1]+w(i+1,mid)<f[mid][k]) f[mid][k]=f[p=i][k-1]+w(i+1,mid);
	}
	solve(l,mid-1,L,p),solve(mid+1,r,p,R);
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(ll i=1; i<=n;i++){
		scanf("%lld",&a[i]);
	}
	memset(f,0x3f,sizeof f);
	for(ll i=1; i<=n; i++){
		f[i][1]=w(1,i);
	}
	for(ll i=2; i<=m; i++){
		k=i,solve(1,n,0,n-1);
	}
	printf("%lld",f[n][m]);
	return 0;
}

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

posted @   _Youngxy  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示