[HNOI2008]玩具装箱TOY

只愿长相守 提交于 2019-12-27 03:54:13

题目链接

学过斜率优化的同学应该知道只是一道板子题,这里我们给出一个不同的解法:

我们定义f[i]f[i]表示考虑到了第ii个点的最小代价是多少,那么我们可以得出这个转移式子:

f[i]=minj=1i(f[j]+w[j][i])f[i]=min_{j=1}^i(f[j]+w[j][i])

其中w[j][i]=(ji+k=1jc[k]L)2w[j][i]=(j-i+\sum_{k=1}^j c[k]-L)^2

看着像不太好优化的样子,但是实际上我们发现f[i]f[i]是凸的,换句话说f[i]f[i]的决策点具有单调性。

这样我们考虑如果我们已经知道了一个状态f[i]f[i],我们考虑它能更新哪些状态,不难发现,由于决策点具有单调性,那么我们可以二分一下它所影响的区间,具体我们画图来解释一下:

一开始所有点的最优决策点:

11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

显然f[2]f[2]的决策点是11,我们考虑22这个决策点能影响的范围:

11111111111111111111111111122222222222222222221111111111111111111111111112222222222222222222

接着我们考虑33,由于决策点具有单调性,那么一定是这样的:

11111111111111111111111111122222222333333333331111111111111111111111111112222222233333333333

或者是:

11111111111111111111111111133333333333333333331111111111111111111111111113333333333333333333

于是我们可以考虑维护一个栈,每个元素(i,j)(i,j)是一个二元组,分别代表区间的左端点和它的决策点是什么,考虑加入一个数:

如果栈顶元素的左端点(原决策点)比现在的决策点更劣,那么我们将这个区间全部舍弃,否则我们就在这个区间二分出改变的点,修改两个元素即可。

考虑到每一个决策点只会入栈和出栈一次,但是由于二分的存在,总复杂度是O(nlogn)O(nlogn)的。

代码实现:

//Optimize
#pragma GCC optimize("Ofast")

//Head File
#include<bits/stdc++.h>
using namespace std;

#define il inline

//Variable
#define ll long long
#define ull unsigned long long
#define db double
#define lb long double

//Debug
#define B cerr<<"Break Point"<<endl;
#define P(x) cerr<<#x<<' '<<"="<<' '<<(x)<<endl;
#define p(x) cerr<<#x<<' '<<"="<<' '<<(x)<<' ';
#define ml(x) cerr<<"Size of array is "<<x*4/1024/1024<<" MB"<<endl;

//Vector
#define vc vector
#define pub push_back
#define pob pop_back
#define vbe(x) x.begin(),x.end()

//Memset
#define ms(x) memset(x,0,sizeof(x))
#define MS(x) memset(x,0x3f3f3f3f,sizeof(x))

//Pair
#define fi first
#define se second

//File

#define fin(x) freopen(x,"r",stdin)
#define fou(x) freopen(x,"w",stdout)
void fio()
{
    #ifndef ONLINE_JUDGE
    freopen("sample.in","r",stdin);
    freopen("sample.out","w",stdout);
    #endif
}
void pti()
{
    double timeuse = clock()*1000.0/CLOCKS_PER_SEC;
    cerr<<"Timeuse "<<timeuse<<"ms"<<endl;
}
void end()
{
    pti();
    exit(0);
}

//Inf
#define INF 0x3f3f3f3f
#define LINF ((long long)(0x3f3f3f3f3f3f3f3f))

//IO
namespace io
{
	const int SIZ=55;int que[SIZ],op,qr;char ch;
	template<class I>
	il void gi(I &w)
    {
        ch=getchar(),op=1,w=0;while(!isdigit(ch)){if(ch=='-') op=-1;ch=getchar();}
        while(isdigit(ch)){w=w*10+ch-'0';ch=getchar();}w*=op;
    }
	template<class I>
	il void print(I w)
    {
        qr=0;if(!w) putchar('0');if(w<0) putchar('-'),w=-w;while(w) que[++qr]=w%10+'0',w/=10;
        while(qr) putchar(que[qr--]);
    }
}
using io::gi;
using io::print;

const int N=5e4+5;

struct node
{
    int pos,b,e;
};

int n,top,stk[N],anc[N];
ll L,c[N],f[N],pre[N];

ll calc(int fa,int x)
{
    ll t=pre[x]-pre[fa]-L-1;
    return f[fa]+t*t;
}
int find(int x)
{
    int l=stk[top],r=n;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(calc(x,mid)<calc(anc[top],mid)) r=mid-1;
        else l=mid+1;
    }
    return l;
}
int main()
{
    fio();
    gi(n),gi(L);
    for(int i=1;i<=n;++i) gi(c[i]);
    for(int i=1;i<=n;++i) pre[i]=pre[i-1]+c[i];
    for(int i=1;i<=n;++i) pre[i]+=i;
    int cur=0;
    stk[++top]=1,++cur;
    for(int i=1;i<=n;++i)
    {
        while(i==stk[cur+1]) ++cur;
        f[i]=calc(anc[cur],i);
        while(calc(i,stk[top])<calc(anc[top],stk[top])) --top;
        int t=find(i);
        if(t<=n) stk[++top]=t,anc[top]=i;
    }
    print(f[n]);
    end();
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!