Loading

CF868F Yet Another Minimization Problem - 决策单调性

题解

\(f_{i,j}\) 为将前 \(j\) 个元素分成 \(i\) 段,其最小的代价。设 \(w(i,j)\) 为把 \([i,j]\) 之间的元素放到一段的代价,则 \(f_{i,j}=\min\limits_{1\le k\le j}\{f_{i-1,k-1}+w(k,j) \}\)

对于任意整数 \(1\le a\le b\le c\le d\le n\),考虑 \([a,d]\) 中任意一种元素的值,设这种元素在 \([l,r]\) 中的出现次数为 \(f[l,r]\)。则:

\[\begin{aligned} w(a,d)+w(b,c)&=f[a,d](f[a,d]-1)/2+f[b,c](f[b,c]-1)/2\\ &=\dfrac{(f[a,b]+f[b,c]+f[c,d])^2-f[a,d]+f[b,c]^2-f[b,c]}{2} \end{aligned}\]

\[\begin{aligned} w(a,c)+w(b,d)=\dfrac{(f[a,b]+f[b,c])^2-f[a,c]+(f[b,c]+f[c,d])^2-f[b,d]}{2} \end{aligned}\]

\[\begin{aligned} &w(a,d)+w(b,c)-(w(a,c)+w(b,d))\\&=\dfrac{(f[a,b]+f[b,c]+f[c,d])^2+f[b,c]^2-(f[a,b]+f[b,c])^2-(f[b,c]+f[c,d])^2}{2}\\&=\dfrac{f[c,d](2f[a,b]+2f[b,c]+f[c,d])-f[c,d](2f[b,c]+f[c,d])}{2}\\&=\dfrac{2f[c,d]f[a,b]}{2}\\&=f[a,b]\times f[c,d]\\&\ge 0 \end{aligned}\]

于是 \(w\) 满足 四边形不等式,由此可知 \(f_i\) 满足 决策单调性

采取分治的方法计算即可。

对于 \(w(i,j)\) 的计算:双指针即可。对于每一次递归,其双指针的移动次数不会超过其可能的决策范围。

总时间复杂度 \(\mathcal{O}(kn\log n)\)

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &_x){
	_x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
	_x*=_f;
}
template<typename T,typename... Args> void Read(T &_x,Args& ...others){
	Read(_x);Read(others...);
}
typedef long long ll;
const int N=1e5+5;
int n,k,a[N];
namespace{
	ll cnt[N],cur;int L,R;
	void Add(int c){
		cur+=cnt[c];++cnt[c];
	}
	void Del(int c){
		if(!cnt[c]) return;
		--cnt[c];cur-=cnt[c];
	}
	ll W(int l,int r){
		while(l<L) Add(a[--L]);
		while(l>L) Del(a[L++]);
		while(r>R) Add(a[++R]);
		while(r<R) Del(a[R--]);
		return cur;
	}
}
ll f[25][N];
void Solve(int l,int r,int L,int R,int cur){
	if(l>r||L>R) return;
	int mid=(l+r)>>1,sep=0;
	For(i,L,min(mid,R)){
		ll w=W(i,mid);
		if(f[cur][mid]>f[cur-1][i-1]+w){
			f[cur][mid]=f[cur-1][i-1]+w,sep=i;
		}
	}
	Solve(l,mid-1,L,sep,cur);Solve(mid+1,r,sep,R,cur);
}
int main(){
	Read(n,k);
	For(i,1,n) Read(a[i]);
	memset(f,0x3f,sizeof f);
	For(i,1,n) f[1][i]=W(1,i);
	For(i,2,k){
		memset(cnt,0,sizeof cnt);
		cur=0,L=0,R=0;
		Solve(1,n,1,n,i);
	}
	printf("%lld\n",f[k][n]);
	return 0;
}
posted @ 2021-09-27 21:03  Alan_Zhao_2007  阅读(36)  评论(0编辑  收藏  举报