决策单调性优化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);
}

浙公网安备 33010602011771号