zoukankan      html  css  js  c++  java
  • BZOJ.4072.[SDOI2016]征途(DP 斜率优化)

    题目链接

    题目要求使得下面这个式子最小((mu=frac{sum_{i=1}^ma_i}{m})是平均数,(a_i)为第(i)段的和): $$frac{sum_{i-1}^m(mu -a_i)^2}{m}*m^2$$
    (m)可以乘进去,得: $$m imessum_{i=1}^m(a_i-frac{sum}{m})^2$$
    注意到其中有(m)(sum),于是连同乘上的(m)一起提出来就是(m imessum_{i=1}^m(a_i^2-2*a_i*frac{sum}{m})+sum^2)
    (sum_{i=1}^m2*a_i)又是一个(sum),于是最终化简为$$m imessum_{i=1}^ma_i^2-sum^2$$
    使(sum_{i=1}^ma_i^2)最小,转移方程是(f[i][j]=f[i-1][k]+(s_j-s_k)^2)
    容易想到有单调性,用斜率优化即可。

    [f_j-f_k+s_j^2-s_k^2leq 2*s_i*(s_j-s_k) ]

    注意初始化。。即到某点时分一组的值。

    下附错误代码(化简不好导致。。)

    //868kb	272ms
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    const int N=3005;
    
    int n,m,sum[N],f[N][2],q[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline int X(int j,int k){
    	return sum[j]-sum[k];
    }
    inline int Y(int j,int k,int s){
    	return f[j][s]-f[k][s]+(sum[j]*sum[j]-sum[k]*sum[k]);
    }
    inline int Calc_F(int i,int j,int s){
    	return f[j][s]+(sum[i]-sum[j])*(sum[i]-sum[j]);
    }
    
    int main()
    {
    	n=read(),m=read();
    	for(int i=1; i<=n; ++i) sum[i]=sum[i-1]+read(),f[i][0]=sum[i]*sum[i];
    	int p=0;
    	for(int j=2; j<=m; ++j)
    	{
    		int h=1,t=1; q[1]=j-1;//这可以初始化为这个,i从j枚举。
    		for(int i=j; i<=n; ++i)
    		{
    			while(h<t && Y(q[h+1],q[h],p)<=2*sum[i]*X(q[h+1],q[h])) ++h;
    			f[i][p^1]=Calc_F(i,q[h],p);
    			while(h<t && 1ll*Y(i,q[t],p)*X(q[t],q[t-1])<=1ll*Y(q[t],q[t-1],p)*X(i,q[t])) --t;
    			q[++t]=i;
    		}
    		p^=1;
    	}
    	printf("%d",m*f[n][p]-sum[n]*sum[n]);
    
    	return 0;
    }
    

    错误做法:

    题目要求使得下面这个式子最小((mu=frac{sum_{i=1}^ma_i}{m})是平均数,(a_i)为第(i)段的和): $$frac{sum_{i-1}^m(mu -a_i)^2}{m}m^2$$
    (m)可以乘进去,得: $$m imessum_{i=1}^m(frac{sum}{m}-a_i)^2$$
    那是个平方,于是写成俩,把(m)乘进一个去,得: $$frac{sum^2}{m}-2
    a_isum+a_i^2m$$
    其中 (a_i=sum_{now}-sum_i)(frac{sum^2}{m})是一个常数,直接用。
    显然(...)具有决策单调性。然后展开一堆式子,得到。。不写了。
    但是不对,暴力也可能和答案差1,因为(sum^2)不一定整除(m),但那个常数的位置还没法弄掉分母,所以计算(f[])的时候就gg。
    怕不是还能用double水

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    const int N=3005;
    
    int n,m,w,sum[N],f[N],f2[N][N],q[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline int X(int j,int k){
    	return m*(sum[j]-sum[k]);
    }
    inline long long Y(int j,int k){
    	return f[j]-f[k]+2*(sum[j]-sum[k])*sum[n]+(sum[j]*sum[j]-sum[k]*sum[k])*m;
    }
    inline int Calc_F(int i,int j){
    	int ai=sum[i]-sum[j];
    	return f[j]+w-2*ai*sum[n]+ai*ai*m;
    }
    
    int main()
    {
    	n=read(),m=read();
    	for(int i=1; i<=n; ++i) sum[i]=sum[i-1]+read();
    	w=sum[n]*sum[n]/m;
    	memset(f2,0x3f,sizeof f2);
    	f2[0][0]=1;
    	for(int i=1; i<=m; ++i)
    	{
    //		int h=1,t=1; q[1]=0;
    		for(int j=i; j<=n; ++j)
    		{
    //			while(h<t && Y(q[h+1],q[h])<=2*sum[j]*X(q[h+1],q[h])) ++h;
    //			f[j]=Calc_F(j,q[h]);
    //			while(h<t && Y(i,q[t])*X(q[t],q[t-1])<=Y(q[t],q[t-1])*X(i,q[t])) --t;
    //			q[++t]=j;
    			for(int k=0; k<j; ++k)
    			{
    				int ai=sum[j]-sum[k];
    				f2[i][j]=std::min(f2[i][j],f2[i-1][k]+w-2*ai*sum[n]+ai*ai*m);
    			}
    		}
    	}
    	printf("%d",f2[m][n]);
    
    	return 0;
    }
    
  • 相关阅读:
    windows中echo的用法
    通过phpMyAdmin写入MySQL,获取webshell
    底部小鱼特效
    kali Linux的简单介绍
    Kali安装gmpy2
    利用kali生成字典的三种方式
    利用kali嗅探HTTP网页用户账户密码
    永恒之蓝(ms017-010)漏洞利用
    如何使用最新Microsoft Edge打开Flash页面
    结对第二次作业
  • 原文地址:https://www.cnblogs.com/SovietPower/p/8694348.html
Copyright © 2011-2022 走看看