zoukankan      html  css  js  c++  java
  • Codeforces 868F. Yet Another Minimization Problem

    Description

    给出一个长度为 (n) 的序列,你需要将它分为 (k) 段,使得每一段的价值和最小,每一段的价值是这一段内相同的数的个数
    题面

    Solution

    容易想到设 (f[i][j]) 表示前 (i) 个数分成 (j) 段的最小代价
    (f[i][j]=min(f[k][j-1]+w(k+1,i)))
    这个(DP)有决策单调性,可以分治优化
    (solve(l,r,L,R)) 表示用区间 ([L,R]) 内的决策去更新 ([l,r]) 的函数,找到 (mid) 的决策点然后分治下去就可以了

    对于 (w) 函数的处理还需要注意:
    我们维护两个全局指针,每一次移动到指定的区间,这样就可以 (O(1)) 转移了,均摊指针的移动次数是 (O(n*log))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+10;
    int n,a[N],m,t[N],p=1,q=0;ll f[N][22],tot=0;
    inline void upd(int l,int r){
    	while(q<r)q++,tot+=t[a[q]],t[a[q]]++;
    	while(p>l)p--,tot+=t[a[p]],t[a[p]]++;
    	while(q>r)t[a[q]]--,tot-=t[a[q]],q--;
    	while(p<l)t[a[p]]--,tot-=t[a[p]],p++;
    }
    inline void solve(int l,int r,int L,int R,int k){
    	if(l==r){
    		for(int i=min(R,r);i>=L;i--)
    			upd(i+1,l),f[l][k]=min(f[l][k],f[i][k-1]+tot);
    		return ;
    	}
    	int mid=(l+r)>>1,st=0;
    	for(int i=min(mid-1,R);i>=L;i--){
    		upd(i+1,mid);
    		if(tot+f[i][k-1]<f[mid][k])f[mid][k]=tot+f[i][k-1],st=i;
    	}
    	if(st)solve(l,mid,L,st,k),solve(mid+1,r,st,R,k);
    	else solve(mid+1,r,L,R,k);
    }
    int main(){
      freopen("pp.in","r",stdin);
      freopen("pp.out","w",stdout);
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)scanf("%d",&a[i]);
      memset(f,127/3,sizeof(f));f[0][1]=0;
      for(int i=1;i<=n;i++)f[i][1]=f[i-1][1]+t[a[i]],t[a[i]]++;
      memset(t,0,sizeof(t));
      for(int i=2;i<=m;i++)
    	  solve(1,n,1,n,i);
      cout<<f[n][m]<<endl;
      return 0;
    }
    
    
  • 相关阅读:
    C# 手机号码隐藏中间四位
    mysql-8.0.20-winx64 绿色版安装教程
    C# 钩子函数使用
    C#反射机制学习
    docker学习笔记二
    从主机A得到id_rsa.pub文件,在主机B创建用户danny加入该文件,实现主机A免密登录主机B
    关于mysql远程登录问题
    playframework 编译打包过程失败
    docker学习笔记一
    记一次tomcat7.0版本启动项目失败问题
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8665982.html
Copyright © 2011-2022 走看看