斜率优化
概述
以前也不是没写过(怎么又写过啊...?),属于是又来补个档.
前文链接:Link.现在看起来写的确实挺乱的,由于懒也不太决定大休整了.
一些链接:
方法
以loj#10191打印文章为例.
先列出 \(\mathcal{DP}\) 方程:设 \(f_i\) 为前 \(i\) 个数划分的最小价值,则显然有:
这个方程是个人都知道要优化到 \(\mathcal{O}(n)/\mathcal{O}(n\log n)\) 的话只能斜率优化\(/\)李超.
但这篇文章就是讲斜率优化的呀,所以例题肯定用斜率是可解的!
将只有 \(i\) 的项移出去,
现在假设有决策点 \(k<j\) ,且 \(j\) 更优.因此一定会有:
移项,
将 \(s_j-s_k\) 除到左边去,
让 \(j,k\) 位置对应(这里其实是笔者没写好,应该一开始就对应的,但懒得改 \(\LaTeX\) 了,凑合着看看吧),
不妨令 \(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]);	
	
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号