#4721. 雕像

题目描述

一排 $n$ 个村庄,试规划 $K$ 个雕像的位置(不一定在村庄里),以最小化每个村庄到最近雕像的距离之和。

题解

考虑暴力 $\text{dp}$ : $f[j][i]$ 表示前 $i$ 个分了 $j$ 段的最小值,转移在i这一维满足决策单调性,但是这样是 $O(nklogn)$ 过不去。

get新知识: $\text{wqs}$ 二分!

参考了一些blog总结了一下,一般是题目中要恰好选出 $k$ 个物品的限制,然后如果没有物品个数限制的话对于每个 $x$ ,它的答案为 $f(x)$ , $(x,f(x))$ 是形如一个凸壳的东西的话就可以考虑 $\text{wqs}$ 二分。可以发现这题答案形式是一个下凸壳。

我们可以列出 $f[i][j]=f[k-1][j-1]+w(k,i)$ 的 $\text{dp}$ 形式,那我们可以考虑二分常数 $c$ ,并且用 $w(k,i)+c$ 代替 $w(k,i)$ ,那是不是相当于在那个下凸壳上又加了一条斜率为 $c$ 的直线的新凸壳,如果去掉 $j$ 这一维只剩 $f[i]$ 的话,那求的就是这个新的凸壳的最小值,我们可以通过记录下路径转移来确定最小值的 $x$ ,如果 $x=k$ 的点在最下方的时候那 $y-kc$ 就是我们想要的答案。

然后 $\text{dp}$ 的转移部分就是决策单调性,可以写一个单调栈+二分解决。

效率 $O(n \times logc \times logn)$ 。(oj挂了不知道代码对不对)

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+5;
int n,g[N],k;
LL s[N],f[N],c,A;
struct O{int l,r,p;}q[N];
LL F(int j,int i){
    if (j>=i) return 2e18;
    return f[j]+s[i]-s[(i+j)>>1]-s[(i+j+1)>>1]+s[j]+c;
}
int J(){
    int u=1,p;q[1]=(O){1,n,0};
    for (int l,r,mid,i=1;i<=n;i++){
        l=1;r=u;p=n+1;
        while(l<r){
            mid=(l+r+1)>>1;
            if (i<q[mid].l) r=mid-1;
            else l=mid;
        }
        f[i]=F(g[i]=q[l].p,i);
        while(u && F(q[u].p,q[u].l)>=F(i,q[u].l)) p=q[u--].l;
        if (u && F(q[u].p,q[u].r)>=F(i,q[u].r)){
            l=q[u].l;r=q[u].r;
            while(l<r){
                mid=(l+r)>>1;
                if (F(q[u].p,mid)>=F(i,mid)) r=mid;
                else l=mid+1;
            }
            p=l;q[u].r=p-1;
        }
        if (p<=n) q[++u]=(O){p,n,i};
    }
    u=0;p=n;
    while(p) p=g[p],u++;
    return u;
}
int main(){
    cin>>n>>k;
    for (int i=1;i<=n;i++)
        scanf("%lld",&s[i]),s[i]+=s[i-1];
    LL l=0,r=1e15;
    while(l<=r){
        c=(l+r)>>1;
        if (J()<k) r=c-1;
        else l=c+1,A=f[n]-k*c;
    }
    printf("%lld\n",A);
    return 0;
}

 

posted @ 2020-02-22 19:13  xjqxjq  阅读(147)  评论(0编辑  收藏  举报