P3628 [APIO2010]特别行动队

斯德哥尔摩

2018-08-04 11:32:34

Personal

[P3628 [APIO2010]特别行动队](https://www.luogu.org/problemnew/show/P3628) 本蒟蒻的第二道斜率优化的$DP$。 首先还是写出一个$O(n^2)$的$DP$:(但是我太菜了连这个都不会。。。) ```cpp for(int i=1;i<=n;i++) for(int j=0;j<i;j++) dp[i]=max(dp[i],dp[j]+f(sum[i]-sum[j])); ``` 其中$f(x)=ax^2+bx+c,sum[i]$是前缀和。 期望$50$($O(n^2)$在洛谷神机上$1s$可过),实测$40$(莫名其妙$WA$了)。 怎么办? **斜率优化**! 假设$j$的转移优于$k$,那么就有: $$dp[j]+f(sum[i]-sum[j])>dp[k]+f(sum[i]-sum[k])$$ 把$f(x)=ax^2+bx+c$带入,合并同类项得: $$dp[j]+a\times sum[j]^2-2a\times sum[i]\times sum[j]-b\times sum[j]>dp[k]+a\times sum[k]^2-2a\times sum[i]\times sum[k]-b\times sum[k]$$ 移项得: $$2a\times sum[i](sum[j]-sum[k])<(dp[j]+a\times sum[j]^2-b\times sum[j])-(dp[k]+a\times sum[k]^2-b\times sum[k])$$ 除过去搞一下: $$sum[i]<\frac{(dp[j]+a\times sum[j]^2-b\times sum[j])-(dp[k]+a\times sum[k]^2-b\times sum[k])}{2a(sum[j]-sum[k])}$$ 然后就可以直接搞了。 附代码: ```cpp #include<iostream> #include<algorithm> #include<cstdio> #define MAXN 1000010 using namespace std; int n; long long a,b,c; long long sum[MAXN],dp[MAXN]; int head,tail,que[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline long long f(long long x){ return a*x*x+b*x+c; } inline double slope(int x,int y){ return (double)((((dp[y]-dp[x])*1.0/(sum[y]-sum[x])-b)*1.0/a)+(sum[x]+sum[y])*1.0); } void work(){ head=1;tail=1; for(int i=1;i<=n;i++){ while(head<tail&&slope(que[head],que[head+1])<=2.0*sum[i])head++; dp[i]=f(sum[i]-sum[que[head]])+dp[que[head]]; while(head<tail&&slope(que[tail],i)<=slope(que[tail-1],que[tail]))tail--; que[++tail]=i; } printf("%lld\n",dp[n]); } void init(){ n=read();a=read();b=read();c=read(); sum[0]=0; for(int i=1;i<=n;i++){ int x=read(); sum[i]=sum[i-1]+x; } } int main(){ init(); work(); return 0; } ```