决策单调性优化dp

对于一类 dp 问题,我们可以发现一些很好的性质:

\(i\) 的决策点是 \(opt_i\) (即 \(i\)\(opt_i\) 转移而来)。

\(\forall ~~i<j\)\(opt_i<opt_j\)

现有的优化方法无法很好的利用这种性质,考虑分治。

\(\forall x\in [l,r],~~opt_x\in [L,R]\)

每次寻找 $ pos = opt_\frac{l+r}2 $ 。

\(\forall x\in [l,\frac{l+r}2-1],~~opt_x\in [L,pos]\)\(\forall x\in [\frac{l+r}2+1,r],~~opt_x\in [pos,R]\)

可以发现这种操作最多进行 \(\log_{2}n\) 次。

直接暴力寻找 \(pos\) 即可。

例题:

CF868F Yet Another Minimization Problem

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

\(2 \leq n \leq 10^5\)\(2 \leq k \leq \min(n,20)\)\(1 \leq a_i \leq n\)

容易发现一个简单的暴力:

\(dp_{i,j}=\min_{p<i}~~dp_{p,j-1} + w_{p+1,i}\)

我们可以发现对于 \(p_1<p_2\) 一定成立 \(w_{p_1,i} \leq w_{p_2,i}\)

于是可以进行决策单调性分治优化。

\(w_{p,i}\)可以使用类似莫队的思路。

求区间贡献
ll calc(int L,int R,int &l,int &r)
{
	while(l>L) now+=s[a[--l]]++;
	while(r<R) now+=s[a[++r]]++;
	while(l<L) now-=--s[a[l++]];
	while(r>R) now-=--s[a[r--]]; 
	return now;
}

分治
void solve(int L,int R,int iL,int iR,int j)
{
	int mid=(L+R)/2,pos=0; 
	ll res=1e18;
	for(int i=max(1,iL);i<=min(mid,iR);i++)
	{
		ll val=dp[i-1][j-1]+calc(i,mid,l,r);
		if(val<res) res=val,pos=i;
	}
	dp[mid][j]=res;
	if(L==R) return ;
	solve(L,mid,iL,pos,j);
	solve(mid+1,R,pos,iR,j);
}
posted @ 2025-08-21 22:14  Sgt_Dante  阅读(2)  评论(0)    收藏  举报