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]);
    	}
    }
    
  • 相关阅读:
    寻找金秋
    两个周末,两个湖
    桂花林上,再读“六项精进”
    锄奸杜幸,穷寇勿追
    招聘所见思考
    Xufun’s Node.js Primer
    我的软件过程,一年再读
    企业的生命期限,和组织的危机感
    头痛,偷闲,拾黄叶
    喝酒这件事,和等绿灯的习惯
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11853535.html
Copyright © 2011-2022 走看看