[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 }

 

posted @ 2017-10-02 09:35  skylee03  阅读(87)  评论(0编辑  收藏  举报