Loading

CF311B Cats Transport 斜率优化 dp

CF311B Cats Transport 斜率优化 dp

链接

这个题的难度在于如何设计枚举顺序,不难发现,按照山丘来枚举或时间来枚举都不好,我们可以按照等待时间来枚举。

这样枚举有一个好处是:当你钦定一定要 恰好 选猫 \(i\) 时,所有在 \(i\) 前面的猫可以顺道带走,所有在 \(i\) 后面的猫都不能带。

状态的设计就非常自然了:设 \(f_{i,j}\) 表示前 \(i\) 个铲屎官拿前 \(j\) 个猫的最优解,则:

\[f_{i,j}=\min\limits_{i-1\le k\le j-1} \{f_{i-1,k}+\sum\limits_{a=k+1}^j (d_j-d_a) \} \]

其中 \(d_a\) 为恰好取猫 \(a\) 所的对应出发时间。

后面那个和式可以写成:

\[\sum\limits_{a=k+1}^jd_a\sum\limits_{a=k+1}^jd_a \]

\(k\times d_j\) 既有 \(k\) 也有 \(j\) ,所以可以用斜率优化。

单调性分析

不难发现,\(d_j\) 因为排序而单调递增。

事实上,对应每个 \(j\) 的最优决策 \(k\) 也是非严格单调递增的。所以我们可以用单调队列去维护下凸壳。

我们来证明一下上面这个事情。

  • 证明:

    用反证法。假设 \(f_{i,j}\) 的最优解为 \(k\)\(f_{i,j+1}\) 的最优解为 \(k'\) ,其中 \(k>k'\) ,所以我们有:

    \[f_{i-1,k}+\sum\limits_{a=k+1}^j(d_{j}-d_a) <f_{i-1,k'}+\sum\limits_{a=k+1}^{j} (d_j-d_a)\\ \Rightarrow f_{i-1,k}+\sum\limits_{a=k+1}^{j+1}(d_j-d_a)<f_{i-1,k'}+\sum\limits_{a=k+1}^{j+1}(d_j-d_a)<f_{i-1,k'}+\sum\limits_{a=k'+1}^{j+1}(d_j-d_a) \]

    这与 \(f_{i,j+1}\) 的最优解为 \(k'\) 矛盾。可知上述结论成立。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define int ll
#define ull unsigned long long
#define N 100010
#define M 110
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,m,p,D[N],sumD[N],f[M][N],sumd[N],d[N],h[N],t[N];
int q[N],l,r;

inline dd calc_y(int id,int k){
    return f[id-1][k]+sumd[k];
}

inline dd calc_x(int k){
    return k;
}

inline dd calc_k_1(int k){
    return d[k];
}

inline dd calc_k_2(int id,int k1,int k2){
    dd x1=calc_x(k1),x2=calc_x(k2);
    dd y1=calc_y(id,k1),y2=calc_y(id,k2);
    return (y1-y2)/(x1-x2);
}

inline void prework(){
    for(int j=1;j<=m;j++) f[1][j]=j*d[j]-sumd[j];
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);read(p);
    for(int i=2;i<=n;i++) read(D[i]),sumD[i]=sumD[i-1]+D[i];
    for(int i=1;i<=m;i++){
        read(h[i]);read(t[i]);
        d[i]=t[i]-sumD[h[i]];
    }
    sort(d+1,d+m+1);
    // printf("d: ");
    // for(int i=1;i<=m;i++) printf("%d ",d[i]);
    // printf("\n");
    for(int i=1;i<=m;i++) sumd[i]=sumd[i-1]+d[i];
    prework();
    // for(int j=1;j<=m;j++) printf("zhuangtai: i:%d j:%d f:%d\n",1,j,f[1][j]);
    for(int i=2;i<=p;i++){
        l=r=0;q[++r]=i-1;
        for(int j=i;j<=m;j++){
            while(l<r-1&&calc_k_2(i,q[l+1],q[l+2])<calc_k_1(j)) l++;
            if(l<r){
                int k=q[l+1];
                // printf("juece: i:%d j:%d k:%d\n",i,j,k);
                f[i][j]=f[i-1][k]+(j-k)*d[j]-sumd[j]+sumd[k];
            }

            while(l<r-1&&calc_k_2(i,q[r-1],q[r])>calc_k_2(i,q[r],j)) r--;
            q[++r]=j;
            // printf("zhuangtai: i:%d j:%d f:%d\n",i,j,f[i][j]);
        }
    }
    printf("%lld",f[p][m]);
    return 0;
}

d

posted @ 2021-07-03 11:31  hyl天梦  阅读(35)  评论(0编辑  收藏  举报