\[\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);
}