其他分享
首页 > 其他分享> > CF868F Yet Another Minimization Problem 题解

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;
}

record

标签:res,Minimization,int,题解,cal,MAXN,CF868F,代价,dp
来源: https://www.cnblogs.com/lxy-2022/p/CF868F-Solution.html