斜率优化

概述

以前也不是没写过(怎么又写过啊...?),属于是又来补个档.

前文链接:Link.现在看起来写的确实挺乱的,由于懒也不太决定大休整了.

一些链接:

膜拜 ksst

很全

方法

loj#10191打印文章为例.

先列出 \(\mathcal{DP}\) 方程:设 \(f_i\) 为前 \(i\) 个数划分的最小价值,则显然有:

\[f_i=\min(f_j+(s_i-s_j)^2+m) \]

这个方程是个人都知道要优化到 \(\mathcal{O}(n)/\mathcal{O}(n\log n)\) 的话只能斜率优化\(/\)李超.

但这篇文章就是讲斜率优化的呀,所以例题肯定用斜率是可解的!

将只有 \(i\) 的项移出去,

\[f_i=\min(f_j+2\times s_is_j+s_j^2)+s_i^2+m \]

现在假设有决策点 \(k<j\) ,且 \(j\) 更优.因此一定会有:

\[f_k+2\times s_is_k+s_k^2>f_j+2\times s_is_j+s_j^2 \]

移项,

\[(f_k+s_k^2)-(f_j+s_j^2)>2s_i(s_j-s_k) \]

\(s_j-s_k\) 除到左边去,

\[\frac{(f_k+s_k^2)-(f_j+s_j^2)}{s_j-s_k}>2s_i \]

\(j,k\) 位置对应(这里其实是笔者没写好,应该一开始就对应的,但懒得改 \(\LaTeX\) 了,凑合着看看吧),

\[\frac{(f_j+s_j^2)-(f_k+s_k^2)}{s_j-s_k}<2s_i \]

不妨令 \(X_j=s_j,Y_j=f_j+s_j^2\) ,则原式相当于是 \((k,j)\) 两点连成的直线的斜率.

由于 \(s_i\) 只会越来越大,每次加入一个新点 \(i\) 后连成得直线斜率也会越来越大.显然会形成一个下凸壳.

最优解一定在队首取到,于是用单调队列维护即可.

\(\mathcal{AC}\)记录:Link.

补充

现在知道要维护上凸壳还是下凸了,将 DP 柿子改造成为 \(y=kx+b\) 形式。所以问题就是,给你若干个点 \((x,y)\),每次询问一个 \(k\),求过所有点的直线中,\(y\) 轴截距 \(b\) 最值为什么。

要维护一些东西:

  • 若求最小值,维护下凸壳,否则维护上凸壳。

  • \(x\) 是否具有单调性:\(x\) 不降从左到右,否则从右向左加点

  • \(k\) 是否具有单调性:把凸壳画出来,看 \(k\) 按照规律变化之后对应凸壳上的点是向左的还是向右的

两种处理方式:

  • \(k,b\) 均单调:单调队列/单调栈
  • \(k\) 不单调,\(b\) 单调:二分+单调队列/单调栈

两种维护:

  • \(x,k\) 移动方向一致:单调队列
  • \(x,k\) 移动方式不一致:单调栈

关于到底维护什么的讨论

对于式子:\(\frac{(f_j+s_j^2)-(f_k+s_k^2)}{s_j-s_k}<2s_i\) :

如果 \(s_i\) 递增,维护下凸壳,否则不具备单调性.

对于式子:\(\frac{(f_j+s_j^2)-(f_k+s_k^2)}{s_j-s_k}>2s_i\) :

如果 \(s_i\) 递减,维护上凸壳,否则不具备单调性.

指路\(\mathcal{HN}\)省队集训\(\mathcal{Day}1\):Link.

这题方程出来后发现 \(a_i\) 明显不具备任何单调性,所以不可斜率优化.

板子写法

附个自己觉得写的还挺好看的板子.

inline ll Yy(ll x){return f[x]+a*s[x]*s[x]-b*s[x];}

inline ll Xx(ll x){return s[x];}

inline ll Y(ll x,ll y){return Yy(x)-Yy(y);}

inline ll X(ll x,ll y){return Xx(x)-Xx(y);}

inline ll pr(ll x){return x*x;}

inline void solve(){

	memset(f,0,sizeof(f));
	for(register int i=1;i<=n;++i) s[i]=s[i-1]+read();
	hd=tl=1,q[tl]=0;
	for(register int i=1;i<=n;++i){
		while(hd+1<=tl&&Y(q[hd+1],q[hd])>=2*a*s[i]*X(q[hd+1],q[hd])) ++hd;
		ll ap=q[hd];f[i]=f[ap]+a*pr(s[i]-s[ap])+b*(s[i]-s[ap])+c;
		while(hd+1<=tl&&Y(i,q[tl])*X(q[tl],q[tl-1])>=Y(q[tl],q[tl-1])*X(i,q[tl])) --tl;
		q[++tl]=i;
	}
	printf("%lld\n",f[n]);	
	
}
posted @ 2022-03-10 10:15  XeniaF  阅读(24)  评论(0)    收藏  举报