题解 P3628 【[APIO2010]特别行动队】
题目链接:Link
Problem
Solution
不难写出方程:
\[f(i)=\min\limits_{0 \le j < i } \{ f(j)+a(s_i-s_j)^2+b(s_i-s_j)+c \}
\]
去掉min函数,展开得:
\[f(i)=f(j)+a*s_i^2 - a*s_i*s_j + a*s_j^2 + b*s_i - b*s_j + c
\]
将仅与j有关的项、乘积项、和其余的分开,得:
\[f(j)+a*s_j^2-b*s_j=2*a*s_i*s_j + f(i) - a*s_i^2 - b* s_i - c
\]
则在这个线性规划模型中,对于每个决策i,决策点位于 $ ( s_i , f(i) + a * s_i^2 - b * s_i ) $ 。
对于每个要求的 $ f(i) $ ,直线斜率为 $ 2 * a * s_i * s_j $ ,截距为 $ f(i) - a * s_i^2 - b * s_i - c $ 。
整个过程中有如下性质:
- $ s_j $ 单调增
- 直线斜率单调减
故可用单调队列维护这个斜率优化过程。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1000005;
int n,Q[maxn],L,R;
LL a,b,c,v[maxn],s[maxn],X[maxn],Y[maxn],f[maxn];
int main()
{
#ifdef local
freopen("pro.in","r",stdin);
#endif
scanf("%d%lld%lld%lld",&n,&a,&b,&c);
for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
for(int i=1;i<=n;i++) s[i]=s[i-1]+v[i];
Q[L=R=1]=0;
for(int i=1;i<=n;i++)
{
while(L<R&&(Y[Q[L+1]]-Y[Q[L]])>=2*a*s[i]*(X[Q[L+1]]-X[Q[L]])) L++;
int j=Q[L];
f[i]=f[j]+a*(s[i]-s[j])*(s[i]-s[j])+b*(s[i]-s[j])+c;
X[i]=s[i]; Y[i]=f[i]+a*s[i]*s[i]-b*s[i];
while(L<R&&(Y[Q[R]]-Y[Q[R-1]])*(X[i]-X[Q[R-1]])<=(Y[i]-Y[Q[R-1]])*(X[Q[R]]-X[Q[R-1]])) R--;
Q[++R]=i;
}
printf("%lld\n",f[n]);
return 0;
}
本作品由happyZYM采用知识共享 署名-非商业性使用-相同方式共享 4.0 (CC BY-NC-SA 4.0) 国际许可协议(镜像(简单版)镜像(完整版))进行许可。
转载请注明出处:https://www.cnblogs.com/happyZYM/p/11629602.html (近乎)全文转载而非引用的请在文首添加出处链接。