zoukankan      html  css  js  c++  java
  • 题解-[SDOI2016]征途

    [SDOI2016]征途

    [SDOI2016]征途

    给定长度为 (n) 的序列 (a{n}),将其分为连续 (m) 段,和分别为 (v{m})(v{m}) 的方差为 (E),求 (left(m^2 imes E ight)_{min})

    (1le nle 3000)(1le sum a_ile 30000)


    复习斜率优化第一题,遇到好多麻烦,写一篇题解记录。


    首先推式探索 (m^2 imes E) 的本质:

    (sum=sum a_i=sum v_i)

    [egin{split} m^2 imes E=&m^2 imes frac{sumlimits_{i=1}^mleft(v_i-frac{sum}{m} ight)^2}{m}\ =&m sumlimits_{i=1}^mleft(v_i-frac{sum}{m} ight)^2\ =&m left(sumlimits_{i=1}^mv_i^2-sumlimits_{i=1}^m2v_icdotfrac{sum}{m}+sumlimits_{i=1}^mfrac{sum^2}{m^2} ight)\ =&m left(sumlimits_{i=1}^mv_i^2-2sumcdotfrac{sum}{m}+mcdotfrac{sum^2}{m^2} ight)\ =&m left(sumlimits_{i=1}^mv_i^2-2cdotfrac{sum^2}{m}+frac{sum^2}{m} ight)\ =&m left(sumlimits_{i=1}^mv_i^2-frac{sum^2}{m} ight)\ =&m sumlimits_{i=1}^mv_i^2-sum^2\ end{split} ]

    很明显 (-sum^2) 是定值,所以要求 (left(m^2 imes E ight)_{min}),应该先求 (left(sumlimits_{i=1}^mv_i^2 ight)_{min})


    考虑到 (n) 很迷你,可以 ( exttt{dp})

    (F_{t,i}) 表示前 (t) 段包含 (a_1sim a_i)(left(sumlimits_{h=1}^tv_h^2 ight)_{min})

    假设 (v_t=sum_{h=j+1}^ia_h)

    可以有递推式:

    [F_{t,i}=min{F_{t-1,j}+v_t^2}=min{F_{t-1,j}+left(sum_{h=j+1}^ia_h ight)^2}(jle i) ]

    如果 (s_i=sumlimits_{h=1}^ia_h),用 (f) 表示 (F_t),用 (g) 表示 (F_{t-1}),那么上式变为:

    [f_i=min{g_j+(s_i-s_j)^2}(jle i) ]


    考虑 (j=k)(j=t) 更优的情况:

    [egin{split} g_k+(s_i-s_k)^2<& g_t+(s_i-s_t)^2\ g_k+s_i^2-2s_is_k+s_k^2<& g_t+s_i^2-2s_is_t+s_t^2\ g_k-2s_is_k+s_k^2<& g_t-2s_is_t+s_t^2\ (g_k+s_k^2)-(g_t+s_t^2)<& 2s_is_k-2s_is_t\ frac{(g_k+s_k^2)-(g_t+s_t^2)}{s_k-s_t}<& 2s_i end{split} ]


    然后老套路,把 ((g_j,g_j+s_j^2)) 当做点,单调队列维护一个下凸壳,实现 ( exttt{dp})

    re int l,r; re vector<int> q(n+7);
    for(re int i=1;i<=n;i++) dp[1][i]=p2(sm[i]); 
    for(re int t=2;t<=m;t++){
    	f=dp[t&1],g=dp[(t&1)^1],l=1,r=0,q[++r]=0; //奇淫技巧①:用指针 f,g 来定位滚动数组
    	for(re int i=1;i<=n;i++){
    		while(l<r&&slope(q[l],q[l+1])<=2.0*sm[i]) l++;
    		//奇淫技巧②:取min维护下凸壳,用<=,取max维护上凸壳,用>=
    		f[i]=g[q[l]]+p2(sm[i]-sm[q[l]]);
    		while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i)) r--; //奇淫技巧③:递推前后两句话用不同比较符号
    		q[++r]=i;
    	}
    }
    

    最后的答案就是 (mcdot f_n-sum^2)


    时间复杂度 (Theta(mn)),空间复杂度 (Theta(n))


    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    #define re register
    #define il inline
    #define mk make_pair
    #define pb push_back
    #define db double
    #define lng long long
    #define fi first
    #define se second
    const int inf=0x3f3f3f3f;
    const lng INF=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=3000;			     /* dp[i][j]:i-th day    	  ***/
    int n,m,dp[2][N+7],*f,*g;		    /**          j-th section     **/
    vector<int> a,sm;			   /***          val min sum()^2  */
    
    //DP
    template<typename T>il T p2(re T x){return x*x;} 
    il db slope(re int x,re int y){ //斜率函数用double
    	return 1.0*((g[x]+p2(sm[x]))-(g[y]+p2(sm[y])))/(sm[x]-sm[y]);
    }
    il int DP(){
    	re int l,r; re vector<int> q(n+7);
    	for(re int i=1;i<=n;i++) dp[1][i]=p2(sm[i]); 
    	for(re int t=2;t<=m;t++){
    		f=dp[t&1],g=dp[(t&1)^1],l=1,r=0,q[++r]=0;
    		for(re int i=1;i<=n;i++){
    			while(l<r&&slope(q[l],q[l+1])<=2.0*sm[i]) l++;
    			f[i]=g[q[l]]+p2(sm[i]-sm[q[l]]);
    			while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i)) r--;
    			q[++r]=i;
    		}
    	}
    	return f[n];
    }
    
    //Main
    int main(){
    	scanf("%d%d",&n,&m),a=sm=vector<int>(n+7);
    	for(re int i=1;i<=n;i++) scanf("%d",&a[i]),sm[i]=sm[i-1]+a[i];
    	printf("%d
    ",m*DP()-p2(sm[n])); //别忘了求最终答案啊
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    HTML 介绍及标签
    MySQL 索引
    子网划分
    网络基础
    python爬虫之12306网站--车站信息查询
    python集合与字典的用法
    python列表与元组的用法
    python条件语句
    python字符串处理以及字符串格式化
    python数据类型分类以及运算类型
  • 原文地址:https://www.cnblogs.com/George1123/p/12589759.html
Copyright © 2011-2022 走看看