CF868F Yet Another Minimization Problem 题解
作者:互联网
CF8658F
题意
一个长度为 \(n\) 的序列,要分为 \(m\) 段,每段代价为段内相同数的对数,求总代价的最小值。
分析
设 \(cal(i,j)\) 表示段 \([i,j]\) 内的相同的数的对数,\(dp_i\) 表示当前最后一段以 \(i\) 为末端的最小总代价,则有转移方程:
\[dp_i=\min\limits_{j=1}^{i-1}\{dp_j+cal(i,j)\} \],需要优化。观察代价函数 \(cal\),如图,
可以发现上面蓝线左侧的黑色部分变到下面红色部分,即从包含变为相交,其代价减小的是上面蓝线右侧的黑色部分对左侧的贡献,增加的是下面蓝线右侧的黑色部分对左侧的贡献,容易发现增加的贡献小于减少的贡献。由于求的是最小值,因此发现代价函数满足包含劣于相交,即满足四边形不等式,因此 dp 满足决策单调性。
发现转移只需从上一层转移,因此可以使用分治优化,而代价函数可以用类似莫队的方法快速从相邻的转移,所以就做完了。
核心代码
int n,m,a[MAXN],L=1,R,res,f[MAXN],dp[MAXN],mp[MAXN];
inline void add(int x){res+=!mp[x]++;}
inline void del(int x){res-=!--mp[x];}
int cal(int l,int r){
while(l>L) del(a[L++]);
while(r<R) del(a[R--]);
while(l<L) add(a[--L]);
while(r>R) add(a[++R]);return res;
}
void solve(int l,int r,int pl,int pr){
if(l>r) return;
int p=0,mid=(l+r)>>1;dp[mid]=0;
for(int i=pl;i<=qmin(pr,mid-1);i++)
if(dp[mid]<f[i]+cal(i+1,mid)) dp[mid]=f[i]+cal(i+1,mid),p=i;
solve(l,mid-1,pl,p);solve(mid+1,r,p,pr);
}
signed main(){
qread(n,m);int i,j;for(i=1;i<=n;i++) qread(a[i]);mp[0]=1;
for(i=1;i<=n;i++) dp[i]=f[i]=cal(1,i);m--;
while(m--){
solve(1,n,0,n-1);
for(i=1;i<=n;i++) f[i]=dp[i];
}printf("%lld\n",dp[n]);
return 0;
}
标签:res,Minimization,int,题解,cal,MAXN,CF868F,代价,dp 来源: https://www.cnblogs.com/lxy-2022/p/CF868F-Solution.html