HDU 3507 Print Article
Problem Description
Zero has an old printer that doesn't work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost
M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost
M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
Input
There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.
Output
A single number, meaning the mininum cost to print the article.
Sample Input
5 5
5
9
5
7
5
Sample Output
230
这也算是一道经典的斜率优化题目,转移方程很好写:
f[i] = min{f[j] + pow2(sum[i]-sum[j])} + m;
带入不等式,发现若j<k,而j优于k,则
f[j] + pow2(sum[i]-sum[j]) < f[k] + pow2(sum[i]-sum[k])
这便意味着
f[j] + pow2(sum[j]) - 2*sum[i]*sum[j] < f[k] + pow2(sum[k]) - 2*sum[i]*sum[k]
所以
f[j] + pow2(sum[j]) - (f[k] + pow2(sum[k]) ) < 2*sum[i] *(sum[j]- sum[k])
f[k] + pow2(sum[k]) - (f[j] + pow2(sum[j]) ) < 2*sum[i] *(sum[k]- sum[j])
斜率就出来了。因为sum[i]单调递增,而sum[k]- sum[j]恒正(这是最关键的一点),所以一旦队首(之前的最优解)比后面的不优,即
f[k] + pow2(sum[j]) - (f[j] + pow2(sum[j]) ) >= 2*sum[i] *(sum[j]- sum[k])
那么这个不等式便从此恒成立了。此时,我们就可以把队首清除。
因为这是一个单调队列,所以同时可以清楚的判断出,应该是上凸还是下凸(此处是下凸)。
但为什么是下凸呢?因为夹在中间的上凸点,显然比两边都不优。比左边不优很好说明,因为
这道题目根本不成问题,套路见下。
#define PN "" #include <cstdio> #include <cstring> #include <algorithm> #include <utility> template<class T> inline void readin(T &res) { static char ch;T f=1; while((ch=getchar())<'0'||ch>'9')if(ch=='-')f=-1; res=ch-48; while((ch=getchar())>='0'&&ch<='9')res=(res<<1)+(res<<3)+ch-48; } template<typename T>inline T max(T a,T b){if(a>b)return a;return b;} template<typename T>inline T min(T a,T b){if(a<b)return a;return b;} template<typename T>inline T abs(T a){if(a<0)return -a;return a;} typedef long long LL; const int N = 500000 + 100; class quque{ private: LL list[N]; int head, tail; public: quque():head(0),tail(0){} void clear(){head=tail=0;} void push(LL x){list[tail++]=x;} void pop(){++head;} void del(){--tail;} LL& operator[](LL pos){return list[head+pos];} LL size(){return tail-head;} }q; LL n, m, f[N], sum[N]; inline LL pow2(LL x){return x*x;} inline LL UP(LL j,LL k) {return f[k]+pow2(sum[k])-f[j]-pow2(sum[j]);} inline LL DOWN(LL j,LL k) {return sum[k]-sum[j];} inline bool check1(LL j, LL k, LL i){return UP(j,k)<=2*sum[i]*DOWN(j,k);} inline bool check2(LL j,LL k,LL i) {return UP(j,k)*DOWN(k,i)>=UP(k,i)*DOWN(j,k);} int main() { //freopen(PN ".in","r",stdin); //freopen(PN ".out","w",stdout); while(~scanf("%lld%lld",&n,&m)) { sum[0]=0; for( LL i = 1; i <= n; i++ ) readin(sum[i]), sum[i]+=sum[i-1]; q.clear(); q.push(0); f[0] = 0;//All the first 0 for( LL i = 1; i <= n; i++ ){ while(q.size()>1 && check1(q[0],q[1],i)) q.pop();//队首后移直到最佳决策点(size>=2才可后移), LL j=q[0]; f[i]=f[j]+pow2(sum[i]-sum[j])+m;//决策转移 while(q.size()>1 && check2(q[q.size()-2],q[q.size()-1],i)) q.del();//Graham-Scan式更新(size>=2才可删点) q.push(i);//插入 } printf("%lld ",f[n]); } return 0; }