A - Print Article
今天刚刚学习了一下斜率dp,感觉还ok,主要就是要推这个斜率,然后利用数据结构来优化。
推荐两篇写的比较好的博客,https://www.cnblogs.com/orzzz/p/7885971.html ----> 这个主要学习这个斜率dp的思路
https://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html ---> 这个主要看代码,中间过程感觉有点问题。
https://blog.csdn.net/qq_37025443/article/details/78986673 ---> 这个也建议看看
自己重新理一下这个思路。
sum[i] 表示从1~i 的前缀和
dp[i]表示输到第 i 个字母的最小的花费
所以转移方程很简单 dp[i]=min(dp[j]+(sum[i]-sum[j])^2+M),因为这个j已经输出了,所以下次考虑j+1~i 所以是sum[i]-sum[j] 而不是sum[i]-sum[j-1]
因为这个n有5e5 所有for两次显然会T,这个就可以利用斜率来优化。
令j>k 如果从 j 转移比从k转移更优就需要满足 dp[j]+(sum[i]-sum[j])^2+M<dp[k]+(sum[i]-sum[k])^2+M
上面式子化简之后就是dp[j]+sum[j-1]^2-(dp[k]+sum[k]^2)<2*sum[i]*(sum[j-1]-sum[k])
然后令F[x]=dp[x]-sum[x]^2
所以就是F[j]-F[k]<sum[i]*2*(sum[j]-sum[k]) 而且要满足 j>k
令G[j,k]=(F[j]-F[k])/(2*(sum[j]-sum[k])
所以如果有 i>j>k 那么如果是G[i,j]>G[j,k] 这样就是一个上凹折线,这个肯定不对
G[i,j]<sum[i],那么就是说i点要比j点优,排除j点。
如果G[i,j]>=sum[i],那么j点此时是比i点要更优,但是同时G[j,k]>G[i,j]>sum[i]。这说明还有k点会比j点更优,同样排除j点。
所以一定是下凸的折现,如果不是的就可以排除。
然后对于G[i,j]>G[j,k]
也有三种情况:
1. G[i,j]>sum[t]>G[j,k] j 比 i 优,j 比 k 优
2. G[i,j]>G[j,k]>sum[t] j 比 i 优,k比 j 优
3.sum[t]>G[i,j]>G[j,k] i 比 j 优 ,j比 k 优
所以我们就用单调队列维护<sum[t] 的最大值即可。
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <queue> #include <vector> #include <string> #define inf 0x3f3f3f3f #define inf64 0x3f3f3f3f3f3f3f3f using namespace std; const int maxn = 5e5 + 10; int que[maxn], dp[maxn]; int sum[maxn]; int n, m; int up(int i,int j) { return dp[i] + sum[i] * sum[i] - (dp[j] + sum[j] * sum[j]); } int down(int i,int j) { return 2 * (sum[i] - sum[j]); } int DP(int i,int j) { return dp[j] + (sum[i] - sum[j])*(sum[i] - sum[j]) + m; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { sum[0] = dp[0] = 0; for (int i = 1; i <= n; i++) scanf("%d", &sum[i]), sum[i] += sum[i - 1]; int tail = 0, head = 0; que[tail++] = 0;//因为可能是前面i个全部作为一段才是最小值 for (int i = 1; i <= n; i++) {//head+1<tail 保证队列里面至少有两个值 while (head + 1 < tail&&up(que[head + 1], que[head]) <= sum[i] * down(que[head + 1], que[head])) head++; dp[i] = DP(i, que[head]); while (head + 1 < tail&&up(i, que[tail - 1])*down(que[tail - 1], que[tail - 2]) <= up(que[tail - 1], que[tail - 2])*down(i, que[tail - 1])) tail--; que[tail++] = i; } printf("%d ", dp[n]); } return 0; }