CF868F Yet Another Minimization Problem

CF868F Yet Another Minimization Problem

题目描述

Solution

一开始可以很容易地写出一个 d p dp dp式子:
f i , j f_{i,j} fi,j表示前 i i i个数分成 j j j段的最小代价,有:
f i , j = min ⁡ k = 1 i − 1 f k , j − 1 + C k + 1 , i f_{i,j}=\min_{k=1}^{i-1}f_{k,j-1}+C_{k+1,i} fi,j=k=1mini1fk,j1+Ck+1,i
C x , y C_{x,y} Cx,y表示求见 a [ x . . y ] a[x..y] a[x..y]中有多少个相同的数对。

我们可以发现这样一个性质:
设有整数 x < y ≤ i x<y\leq i x<yi
显然 Δ C x , i > Δ C y , i \Delta C_{x,i}>\Delta C_{y,i} ΔCx,i>ΔCy,i,也就是说在前面的 C C C增长比较快。

考虑决策单调性:
若存在整数 x < y ≤ i x<y\leq i x<yi,使得
f x , j + C x + 1 , i > f y , j + C y + 1 , i f_{x,j}+C_{x+1,i}>f_{y,j}+C_{y+1,i} fx,j+Cx+1,i>fy,j+Cy+1,i
则对于所有的 i ′ > i i'>i i>i,显然有
f x , j + C x + 1 , i ′ > f y , j + C y + 1 , i ′ f_{x,j}+C_{x+1,i'}>f_{y,j}+C_{y+1,i'} fx,j+Cx+1,i>fy,j+Cy+1,i
也就是说,如果前面的比后面的劣,那么它在之后的转移中都不会作为最优决策点出现了。

这里就可以用决策单调性的一个套路做法——分治。
若我们要求的答案为 f x . . y , j f_{x..y,j} fx..y,j,当前的决策点区间为 [ X , Y ] [X,Y] [X,Y]
我们可以先对于 m i d = ⌊ x + y 2 ⌋ mid=\lfloor \frac{x+y}{2} \rfloor mid=2x+y求出 f m i d , j f_{mid,j} fmid,j以及它的最优决策点 k k k,这一步可以暴力枚举 X . . Y X..Y X..Y求得,时间复杂度 O ( Y − X ) O(Y-X) O(YX)

然后显然 f x . . m i d − 1 , j f_{x..mid-1,j} fx..mid1,j的最优决策点 k ′ ≤ k k'\leq k kk f m i d + 1.. y , j f_{mid+1..y,j} fmid+1..y,j的最优决策点 k ′ ≥ k k'\geq k kk,分治计算即可。

这里转移中要用到的 C x , y C_{x,y} Cx,y可以类似莫队的方法,每次或减去加入一个数,计算贡献的方式暴力求。

显然分治一遍的时间复杂度为 T ( n ) = T ( n / 2 ) + O ( n ) = O ( n l g n ) T(n)=T(n/2)+O(n)=O(nlgn) T(n)=T(n/2)+O(n)=O(nlgn),一共要做 m m m遍,所以总时间复杂度为 O ( m n l g n ) O(mnlgn) O(mnlgn)

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>

#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second

using namespace std;

template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }

typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;

const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
ll C=0,f[21][MAXN];
int n,m,L=1,R=0,a[MAXN],cnt[MAXN];
ll getans(int l,int r) 
{
	while (L>l) C+=cnt[a[--L]]++;
	while (L<l) C-=--cnt[a[L++]];
	while (R<r) C+=cnt[a[++R]]++;
	while (R>r) C-=--cnt[a[R--]];
	return C;
}
void solve(int now,int x,int y,int X,int Y)
{
	int mid=(x+y)>>1,t;
	f[now][mid]=loo;
	for (int i=X;i<=Y;i++) t=upmin(f[now][mid],f[now-1][i]+getans(i+1,mid))?i:t;
	if (x==y) return;
	solve(now,x,mid,X,t);
	solve(now,mid+1,y,t,Y);
}
int main()
{
	n=read(),m=read(),L=1,R=C=0;
	for (int i=1;i<=n;i++) a[i]=read(),f[1][i]=getans(1,i);
	for (int i=2;i<=m;i++) solve(i,1,n,1,n);
	printf("%lld\n",f[m][n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值