• [HNOI2008]玩具装箱TOY /【模板】斜率优化


    [HNOI2008]玩具装箱TOY /【模板】斜率优化

    一.题目简述

    ​ 给定一组数列,要把他们分成几组,代每个区间[x, y]的代价是 ((y-x+sum_{i=x}^ya[i]-L)^2).

    二.解题思路

    ​ 这题转移的方程都告诉了,可是还是做不起。不过看样子是个DP题,那我们来推方程。

    ​ 先把令人厌恶的(sum)去掉,使用前缀和来代替。

    [f[i]=min(f[j]+(i-j+sum[i]-sum[j]-1-L)^2) ]

    (其实可以先去掉min不看)先来手算一下复杂度为(O(n^2)),数据范围没得玩。于是我们果断使用单调队...列。不行啊用不了啊喂!!由于有平方,混进去了一个混着(i,j)的项,不能用单调队列。此时隆重请出今天的主角:斜率优化

    三.斜率优化

    ​ 所谓斜率,就是(y=kx+b)中的k罢了,但是这个东西要怎么优化呢?先将整个式子化简,有了方便有如下两条

    • A[i]=sum[i]+i;
    • B[i]=A[i]+L+1;

    那么原式可以化简为

    [f[i]=f[j]+(i-j+sum[i]-sum[j]-1-L)^2;\ f[i]=f[j]+(A[i]-B[j])^2\ f[i]=f[j]+A[i]^2+B[j]^2-2*A[i]*B[j]\ 2*A[i]*B[j]+f[i]-A[i]^2=f[j]+B[j]^2; ]

    来考虑一下我们手里的条件与目标:(A[i],B[j],f[j]),齐全,目标:(f[i])。可以直接求出,而我们现在要做的是优化,即要以最快的速度找出一个或少量的j来推出f[i].

    话题回到斜率,先决条件已充分,现在要找出斜率。我们是要用j推i,所以需要将i,j分开,这句话的数学体现为

    [2*A[i]*x+f[i]-A[i]^2=y ]

    可以发现x,y都是只跟j有关的项,剩下的项全都是跟i有关的(上面那句话的意思就是这个)。有了这个式子就可以进行计算了,请想象:我手头有一个斜率为(2*A[i])的直线,但是与y轴的交点未知,我可以令他过某些点(这些点都是之前算出来的((B[j],f[j]+B[j]^2))),以此来计算出对应的交点以及f[i],在题目的期望下要求f[i]最小。

    [f[i]=b+A[i]^2 ]

    b为与y轴交点坐标,(A[i]^2)
    常数,所以在这道题中斜线要尽量的靠下且靠右

    四.数形结合

    ​ 现在来形象一点。

    根据上文所提,相当于找一个所谓“右下角”的点,最后找到的点体现出来的性质就应该是他和左边的点的斜率<2*a[i]<他与右边点的斜率,可以用这个来作为寻找的依据。

    ​ 又有几点

    • A[i]是单调递增的,所以在i之后的点必然只会选择i的决策点之后的点,所以在i之前的决策点可以删掉
    • 围成的凸要尽量大,即新加入的点形成的斜率如果小于之前的形成的就可以代替了(这句还是看代码吧,解释的很麻烦)

    (其实找点的时候可以二分)

    五.CODE

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<deque>
    #include<algorithm>
    using namespace std;
    #define m_for(i,a,b) for(int i=a;i<=b;++i)
    #define ll long long
    ll read(){
    	ll res=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')res=res*10+(ch-'0'),ch=getchar();
    	return res*f;
    }
    const int MAXN=500005;
    ll n,l,c[MAXN],s[MAXN],f[MAXN];
    inline ll make_y(int i){
    	return f[i]+(s[i]+i+l+1)*(s[i]+i+l+1);
    }
    inline ll make_x(int i){
    	return (s[i]+i+l+1);
    }
    inline ll make_k(int x,int y){
    	return (make_y(x)-make_y(y))/(make_x(x)-make_x(y));
    }
    int q[MAXN],head=1,tail=1;
    int main(){
    	n=read();l=read();
    	m_for(i,1,n)s[i]=s[i-1]+(c[i]=read());
    	m_for(i,1,n){
    		ll ai=s[i]+i;
    		while(head<tail&&make_k(q[head],q[head+1])<2*ai)head++;
    		int j=q[head];
    		f[i]=f[j]+(s[i]-s[j]+i-j-1-l)*(s[i]-s[j]+i-j-1-l);
    		while(head<tail&&make_k(q[tail],q[tail-1])>make_k(i,q[tail-1]))tail--;
    		q[++tail]=i;
    	}
    	cout<<f[n];
    }
    
  • 相关阅读:
    Core Data 入门
    iOS布局之Auto Layout
    iOS 布局之 Springs and Struts
    Soul 学习笔记---使用 nacos 实现数据同步上篇(七)
    Soul 学习笔记---使用 zookeeper 实现数据同步(六)
    Soul 学习笔记---数据同步 websocket 连接建立过程分析(五)
    Soul 学习笔记---soul 数据同步的浅显分析(四)
    Soul学习笔记---运行 soul-examples-dubbo(三)
    Soul学习笔记---运行 soul-examples-http(二)
    windows下安装zookeeper 及 遇到的问题---打开zkServer.cmd闪退,此时不应有 Javajdk1.8.0_144
  • 原文地址:https://www.cnblogs.com/clockwhite/p/12244672.html
Copyright © 2020-2023  润新知