• #分治,决策单调性dp#CF868F Yet Another Minimization Problem


    题目

    给定一个序列 (a),要把它分成 (k) 个子段。((nleq 10^5,kleq 20))

    每个子段的费用是其中相同元素的对数。求所有子段的费用之和的最小值。


    分析

    有一个很明显的(dp)就是设(dp[i][k])表示前(i)个数分成(k)段的费用之和最小值,
    那么(dp[i][k]=min{dp[j][k-1]+calc(j+1,i)})
    可以发现(dp[i][k])不会因为(dp[i'][k])而改变,用分治解决
    并且无法在(O(1))时间内求出来,并且应该是具有决策单调性的,
    考虑calc函数用类似于莫队的方法求解,那么就可以做到(O(knlog_2n))


    代码

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #define rr register
    using namespace std;
    typedef long long lll;
    const int N=100011;
    lll dp[N],f[N],now;
    int a[N],cnt[N],n,m,Le=1,Ri;
    inline signed iut(){
    	rr int ans=0; rr char c=getchar();
    	while (!isdigit(c)) c=getchar();
    	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    	return ans;
    }
    inline lll answ(int l,int r){
    	while (Le<l) now-=--cnt[a[Le++]];
    	while (Le>l) now+=cnt[a[--Le]]++;
    	while (Ri<r) now+=cnt[a[++Ri]]++;
    	while (Ri>r) now-=--cnt[a[Ri--]];
    	return now;
    }
    inline void dfs(int l,int r,int L,int R){
    	if (l>r) return;
    	rr int mid=(l+r)>>1,MID=L;
    	rr lll ans=1ll<<60;
    	for (rr int j=L;j<mid&&j<=R;++j){
    	    rr lll t=answ(j+1,mid);
    	    if (f[j]+t<ans) ans=f[j]+t,MID=j;
    	}
    	dp[mid]=ans;
    	dfs(l,mid-1,L,MID),dfs(mid+1,r,MID,R);
    }
    signed main(){
    	n=iut(); m=iut();
    	for (rr int i=1;i<=n;++i) a[i]=iut();
    	for (rr int i=1;i<=n;++i) dp[i]=answ(1,i);
    	for (rr int i=2;i<=m;++i)
    		memcpy(f,dp,sizeof(dp)),dfs(1,n,1,n);
    	return !printf("%lld",dp[n]);
    }
    
  • 相关阅读:
    Java Swing3-MyDialog的基本实现
    Java IO4 实现凯撒密码加密解密文本文件
    POJ 3869 条件概率
    2017年校招全国统一模拟笔试(第三场)编程题集合
    POJ 2800 Joseph’s Problem 数论找规律
    数据结构期末考试再复习
    Java Swing2 基本登录框
    Java Swing1 基本框架
    Java 多线程1 烧水开水喝茶案例
    Java IO3 把一个图片写进txt,再写出还原图片
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/14979937.html
Copyright © 2020-2023  润新知