P2134百日旅行+斜率优化入门

\[\begin{align*} f_i=&min(f_{i-1}+q,min^{i-2}_{j=0}(f_j+p\times(i-j-1)^2)+q)\\ g_i=&min^{i-2}_{j=0}(f_j+p\times(i-j-1)^2)+q\\ f_i=&min(f_{i-1}+q,g_i) \end{align*} \]

对于\(g_i\)我们有

\[g_i=min^{i-2}_{j=0}(f_j+(i^2-2i+1)p+(-2ij+j^2+2j)p)+q\\ g_i=(i-1)^2p+min^{i-2}_{j=0}((f_j+j^2p+2jp)-2ijp)+q\\ l_i=f_i+i^2p+2ip\\ t_i=\min^{i-2}_{j=0}(l_j-2ijp)\\ g_i=(i-1)^2p+tmp+q\\ \]

试试斜率优化

\[\begin{align} &\forall a_j<a_k,\\ &l_j-2ijp<l_k-2ikp\\ &l_j-l_k<2ijp-2ikp\\ &l_j-l_k<2ip(j-k)\\ \because &j<k\\ \therefore&\frac{l_j-l_k}{j-k}>2ip \end{align} \]

\(j\)\(k\)当前更优,在之后也一定更优

几个一开始没想明白的问题:

1.为什么维护下凸包?

考虑最优解是拿一根斜率\(=2ip\)的直线向上碰到的第一个点,易知不是下凸包上的点不可能作为解

2.为什么\(>2ip\)删掉?怎么找下凸包和\(k=2ip\)切点?删除后还会作为解吗?

考虑是切凸包,\(>2ip\)肯定要删掉,这样就可以每次看队首来找切点;最后一点完全没理解斜率优化本质

单调队列搞一搞就\(O(n)\)了,,,虽然这个数据\(n^2\)也能过,但是重在学算法吧只能说

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mn=2e5+5;
int n,p,q,ans;
int f[mn],l[mn];
int t;
deque<int> dq;
signed main()
{
	scanf("%lld%lld%lld",&n,&p,&q);
	int t=q/p;
	f[0L]=0L;
	f[1]=q;
	l[1]=f[1]+3*p;
	t=0;
	for(int i=2;i<=n+1;i++)
	{
		f[i]=f[i-1]+q;
		dq.push_back(t);
		t=dq.front();
		dq.pop_front();
		while(!dq.empty())
		{
			if(((l[t]-l[dq.front()])*1.0L/(t-dq.front()))<=2*i*p)
			{
				t=dq.front();
				dq.pop_front();
			}
			else
			{
				break;
			}
		}
		f[i]=min(f[i],f[t]+p*(i-t-1)*(i-t-1)+q);
		l[i]=f[i]+i*i*p+2*i*p;
		dq.push_front(t);
		t=dq.back();
		dq.pop_back();
		while(!dq.empty())
		{
			if((l[dq.back()]-l[t])*1.0L/(dq.back()-t)>=(l[t]-l[i-1])*1.0L/(t-i+1))
			{
				t=dq.back();
				dq.pop_back();
			}
			else
			{
				break;
			}
		}
		dq.push_back(t);
		t=i-1;
	}
	printf("%lld",f[n+1]-q);
}
posted @ 2025-02-08 19:53  ikusiad  阅读(10)  评论(0)    收藏  举报