题目大意
一个数轴上有n对点,第
你需要在数轴上任意位置选择K个点,然后将所有点移动到这
请你选择这K个点,最小化每个点移动距离之和。
题目分析
将每一对点看成数轴上的一个区间,分析发现,不管怎么移动点,两个点在移动过程中,要么一个点跨过该区间,要么两个点在区间内汇合,总之一对点的移动距离和至少是区间长度,至于出来的部分,如果区间内有关键点,那么就没有额外的消耗,否则一定是区间其中的一个端点与其距离最近的关键点的距离的两倍。
我们撇开区间本身造成的贡献(最后再加上去),考虑如何求上面所说的“多出来的部分”,为了方便计算我们先不将上面那个距离乘二,最后再乘上去。
于是现在我们的问题是,有若干个数轴上的区间,你要加入K个关键点,答案的计算如下:
可以发现,一定存在一种最优解使得关键点都取值在数轴上原有的点。
考虑dp,令fi,j表示我们放入了i个关键点,当前做到了大小次序为
令
dp方程如下:
这样做是O(n2Klogn)的,考虑怎么优化。
可以发现,令k为使
那么使用经典的决策单调性分治套路,每次计算出中点的取值点,然后分治两边,即可做到O(nKlog2n)。
注意使用各种离散化来卡常。
代码实现
#include <algorithm>
#include <iostream>
#include <climits>
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long LL;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const LL INF=LLONG_MAX/2;
const int N=100050;
const int X=N<<1;
const int LGN=17;
const int S=N*LGN;
struct chairman_tree
{
int son[S][2],size[S];
LL sum[S];
int tot;
int newnode(int rt)
{
sum[++tot]=sum[rt],size[tot]=size[rt];
son[tot][0]=son[rt][0],son[tot][1]=son[rt][1];
return tot;
}
void init(){tot=0;}
void modify(int &rt,int rt0,int x,int l,int r,int delta)
{
rt=newnode(rt0);
++size[rt],sum[rt]+=delta;
if (l==r) return;
int mid=l+r>>1;
if (x<=mid) modify(son[rt][0],son[rt0][0],x,l,mid,delta);
else modify(son[rt][1],son[rt0][1],x,mid+1,r,delta);
}
void query(int rt,int rt0,int x,int l,int r,int &CNT,LL &SUM)
{
if (!x) return;
if (!(size[rt]-size[rt0])) return;
if (l==r)
{
if (x==l) CNT+=size[rt]-size[rt0],SUM+=sum[rt]-sum[rt0];
return;
}
int mid=l+r>>1;
if (x<=mid) query(son[rt][0],son[rt0][0],x,l,mid,CNT,SUM);
else CNT+=size[son[rt][0]]-size[son[rt0][0]],SUM+=sum[son[rt][0]]-sum[son[rt0][0]],query(son[rt][1],son[rt0][1],x,mid+1,r,CNT,SUM);
}
}t[2];
int a[N],b[N],newa[N],newb[N],news[N],xval[N],sval[N];
int root[X][2];
LL f[X],g[X];
int n,K,xtot,stot,xcnt,scnt;
LL ans;
struct data
{
int key,id;
bool tp;
bool operator<(data const x)const{return key<x.key;}
}srt[X];
void pre()
{
for (int i=1;i<=n;++i) srt[++stot].key=a[i]+b[i],srt[stot].id=i;
sort(srt+1,srt+1+stot),srt[0].key=-2000000001,scnt=0;
for (int i=1;i<=stot;++i) sval[news[srt[i].id]=scnt+=srt[i].key!=srt[i-1].key]=srt[i].key;
for (int i=1;i<=n;++i)
{
srt[++xtot].key=a[i],srt[xtot].id=i,srt[xtot].tp=0;
srt[++xtot].key=b[i],srt[xtot].id=i,srt[xtot].tp=1;
}
sort(srt+1,srt+1+xtot),srt[0].key=-1000000001,xcnt=0,xval[0]=-2000000001;
t[0].init(),t[1].init();
int lstl=0,lstr=0,lrtl=0,lrtr=0;
for (int i=1;i<=xtot;++i)
{
xval[xcnt+=srt[i].key!=srt[i-1].key]=srt[i].key;
if (srt[i].tp)
{
newb[srt[i].id]=xcnt;
for (int j=lstr+1;j<xcnt;++j) root[j][1]=root[j-1][1];
t[1].modify(root[xcnt][1],lrtr,news[srt[i].id],1,scnt,b[srt[i].id]);
lstr=xcnt,lrtr=root[xcnt][1];
}
else
{
newa[srt[i].id]=xcnt;
for (int j=lstl+1;j<xcnt;++j) root[j][0]=root[j-1][0];
t[0].modify(root[xcnt][0],lrtl,news[srt[i].id],1,scnt,a[srt[i].id]);
lstl=xcnt,lrtl=root[xcnt][0];
}
}
for (int i=lstl+1;i<=xcnt;++i) root[i][0]=root[i-1][0];
for (int i=lstr+1;i<=xcnt;++i) root[i][1]=root[i-1][1];
}
int search(int x)
{
int ret=0,l=1,r=scnt;
for (int mid;l<=r;)
{
mid=l+r>>1;
if (sval[mid]<=x) l=(ret=mid)+1;
else r=mid-1;
}
return ret;
}
void dp(int l,int r,int st,int en)
{
if (l>r) return;
int mid=l+r>>1,p;
for (int i=st;i<=en&&i<=mid;++i)
{
int CNT;LL SUM,tmp=0;
CNT=0,SUM=0,t[0].query(root[mid][0],i?root[i-1][0]:0,search(!i?INT_MIN:xval[i]+xval[mid]),1,scnt,CNT,SUM);
tmp+=SUM-1ll*CNT*xval[i];
CNT=0,SUM=0,t[1].query(root[mid][1],i?root[i-1][1]:0,search(!i?INT_MIN:xval[i]+xval[mid]),1,scnt,CNT,SUM);
CNT=t[1].size[root[mid][1]]-t[1].size[i?root[i-1][1]:0]-CNT,SUM=t[1].sum[root[mid][1]]-t[1].sum[i?root[i-1][1]:0]-SUM;
tmp+=1ll*CNT*xval[mid]-SUM+g[i];
if (f[mid]>tmp) f[mid]=tmp,p=i;
}
dp(l,mid-1,st,p),dp(mid+1,r,p,en);
}
void calc()
{
ans=INF;
for (int i=1;i<=xcnt;++i) f[i]=INF;
g[0]=0;
for (int k=1;k<=K;++k)
{
for (int i=1;i<=xcnt;++i) g[i]=f[i],f[i]=INF;
dp(1,xcnt,0,xcnt);
int CNT=0;LL SUM=0;
for (int i=xcnt,cur=xtot;i>=1;--i)
{
for (;cur&&srt[cur].key>=xval[i];--cur) if (!srt[cur].tp) ++CNT,SUM+=a[srt[cur].id];
ans=min(ans,f[i]+SUM-1ll*CNT*xval[i]);
}
}
ans*=2;
}
int main()
{
freopen("shoes.in","r",stdin),freopen("shoes.out","w",stdout);
n=read(),K=read();
for (int i=1;i<=n;++i)
{
a[i]=read(),b[i]=read();
if (a[i]>b[i]) swap(a[i],b[i]);
}
pre(),calc();
for (int i=1;i<=n;++i) ans+=b[i]-a[i];
printf("%lld\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}