zoukankan      html  css  js  c++  java
  • 打印文章

    https://loj.ac/problem/10191

    题目描述

      给出(N)个单词,每个单词有个非负权值(C_i),现要将它们分成连续的若干段,每段的代价为此段单词的权值和的平方,还要加一个常数M,即((sum C_i)^2+M)。现在想求出一种最优方案,使得总费用之和最小。

    思路

      连续的若干段单词,我们显然可以列出朴素的状态转移方程

    [f[i]=min{f[j]+(sum[i]-sum[j])^2+M} ]

      很容易化为一次函数的式子

    [f[j]+sum[j]^2=2*sum[i]*sum[j]-sum[i]^2-M+f[i] ]

      显然这就是要截距最小,而且具有单调性,直接维护下凸壳即可。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll N=5e5+10;
    
    ll read()
    {
    	ll res=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
    	return res*w;
    }
    void write(ll x)
    {
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    }
    void writeln(ll x)
    {
    	write(x);
    	putchar('
    ');
    }
    
    ll s[N],q[N],f[N];
    ll X(ll x){return s[x];}
    ll Y(ll x){return f[x]+s[x]*s[x];}
    ll K(ll x){return 2*s[x];}
    int main()
    {
    	ll n,m;
    	while(~scanf("%lld",&n))
    	{
    		m=read();
    		for(ll i=1;i<=n;i++)
    			s[i]=s[i-1]+read();
    		ll l=0,r=0;
    		for(ll i=1;i<=n;i++)
    		{
    			while(l<r&&(Y(q[l+1])-Y(q[l]))<=K(i)*(X(q[l+1])-X(q[l])))l++;
    			f[i]=f[q[l]]+(s[i]-s[q[l]])*(s[i]-s[q[l]])+m;
    			while(l<r&&(Y(q[r])-Y(q[r-1]))*(X(i)-X(q[r]))>=(Y(i)-Y(q[r]))*(X(q[r])-X(q[r-1])))r--;
    			q[++r]=i;
    		}
    		writeln(f[n]);
    	}
    }
    
  • 相关阅读:
    《XXX重大技术需求征集系统》的可用性和可修改性战术分析
    淘宝网的软件质量属性分析
    软件架构师如何工作
    PHP 运算符
    PHP函数
    PHP自定义函数
    Mysql 允许外连
    PHP 小练习题持续更新
    文本文件编辑命令
    工作目录切换命令、打包压缩文件命令
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11853535.html
Copyright © 2011-2022 走看看