[BZOJ3156]防御准备
题目大意:
给你$n$个格子,每个格子可以放一个人或建筑,
放一个建筑的代价为$a_i$,放一个人的代价为这个格子与右边第一个有建筑的格子的距离,
要求每个格子上都得放一个东西,
问最小代价。
思路:
根据题意,我们可以列出状态转移方程$f_i=\displaystyle{\min_{j<i}}\{f_j+\displaystyle{\sum_{k=j+1}^{i}}(i-k)\}+a_i$。
其中$\displaystyle{\sum_{k=j+1}^{i}}(i-k)$可以高斯求和。
接下来考虑两个状态$j$和$k$,
如果j比k优,那么一定有$f_j+\frac{(i-j-1)\times(i-j)}{2}<f_k+\frac{(i-k-1)\times(i-k)}{2}$。
整理得$f_j-f_k+\frac{j^2+j}{2}-\frac{k^2+k}{2}<i(j-k)$。
化为斜率式,就变成了$\frac{f_j-f_k+\frac{j^2+j}{2}-\frac{k^2+k}{2}}{j-k}<i$。
然后用单调队列维护下凸包,然后就WA了。
发现实际上$j<k$,据说移到左边要变号?(初一学的全还给老师了)
所以实际上应该$\frac{f_j-f_k+\frac{j^2+j}{2}-\frac{k^2+k}{2}}{j-k}>i$。
1 #include<cstdio> 2 #include<cctype> 3 inline int getint() { 4 register char ch; 5 while(!isdigit(ch=getchar())); 6 register int x=ch^'0'; 7 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 8 return x; 9 } 10 typedef long long int64; 11 const int N=1e6+1; 12 int q[N],h,t; 13 int64 f[N]; 14 inline double slope(const int64 &j,const int64 &k) { 15 return (f[j]+(j*j+j)/2-f[k]-(k*k+k)/2)/(double)(j-k); 16 } 17 int main() { 18 int n=getint(); 19 for(register int i=1;i<=n;i++) { 20 int a=getint(); 21 while(h<t&&slope(q[h],q[h+1])<i) h++; 22 f[i]=f[q[h]]+(int64)(i-q[h]-1)*(i-q[h])/2+a; 23 while(h<t&&slope(q[t-1],q[t])>slope(q[t],i)) t--; 24 q[++t]=i; 25 } 26 printf("%lld\n",f[n]); 27 return 0; 28 }