#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; }