【洛谷6246】[IOI2000] 邮局 加强版(WQS二分+决策单调性)
- 一个数轴上有\(n\)个村庄,你可以设立\(k\)个邮局,要求所有村庄到离它最近的邮局距离之和最小。
- \(k\le n\le 5\times10^5\)
\(WQS\)二分+决策单调性
我们肯定会把村庄划分为\(k\)段,然后选择每一段所有坐标的中位数位置设立邮局。
于是最暴力的\(DP\)就是设\(f_{i,j}\)表示把前\(i\)个村庄划分成\(j\)段的最小代价。
首先容易想到\(WQS\)二分,这样一来就去掉了设立\(k\)个邮局这个限制。
然后发现这玩意儿显然具有决策单调性,直接开个队列维护决策,每次二分超越时刻即可。
代码:\(O(nlognlogV)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define LL long long
using namespace std;
int n,k,a[N+5],g[N+5];LL s[N+5],f[N+5];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
I LL Calc(CI j,CI i) {RI mid=j+1+i>>1;return f[j]+(1LL*a[mid]*(mid-j)-(s[mid]-s[j]))+((s[i]-s[mid])-1LL*a[mid]*(i-mid));}//在(j,i]中设立邮局
I int Find(CI j,CI k) {RI l=k+1,r=n+1,mid;W(l^r) mid=l+r-1>>1,Calc(j,mid)>=Calc(k,mid)?r=mid:l=mid+1;return r;}//二分超越时刻
int q[N+5],p[N+5];I int Check(CI x)//决策单调性优化DP检验
{
q[1]=0;for(RI i=1,H=1,T=1;i<=n;++i)
{
H^T&&p[H+1]==i&&++H,f[i]=Calc(q[H],i)+x,g[i]=g[q[H]]+1;//转移
W(H^T&&p[T]>=Find(q[T],i)) --T;q[++T]=i,p[T]=Find(q[T-1],i);//队列中维护有效决策
}return g[n];
}
int main()
{
RI i;for(read(n,k),i=1;i<=n;++i) read(a[i]),s[i]=s[i-1]+a[i];
RI l=0,r=1e9,mid;W(l^r) Check(mid=l+r+1>>1)>=k?l=mid:r=mid-1;return Check(l),printf("%lld\n",f[n]-1LL*k*l),0;//WQS二分
}
待到再迷茫时回头望,所有脚印会发出光芒