题意
一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B。
每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000。相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度。区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对。
城市中有 N 个居民。第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 K 座横跨河流的大桥。
由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。当政府建造最多 K 座桥之后,设 Di 表示第 i 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 D1+D2+⋯+DN 最小。
K=2,1≤N≤100000
分析
首先问题可以转化成给n个区间,然后在数轴上找两个点,使得所有区间到离其最近的点的距离和最小。
如果k=1的话,直接找中位数即可。
若k=2的话,我们可以把区间按照中位数来排序,那么一定有一个最优的分割点满足前缀全部选第一个点,后缀全部选第二个点。
那么只要枚举分割点然后用权值线段树来维护中位数即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=200005;
int l[N],r[N],n,m,a[N],a1,f1;
struct data{double val;int id;}f[N];
struct Sig_tree
{
int root,sz;
struct tree{int l,r,size;LL s;}t[N*30];
void ins(int &d,int l,int r,int x,int y)
{
if (!d) d=++sz;
t[d].s+=x*y;t[d].size+=y;
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) ins(t[d].l,l,mid,x,y);
else ins(t[d].r,mid+1,r,x,y);
}
int kth(int d,int l,int r,int k)
{
if (l==r) return l;
int mid=(l+r)/2;
if (t[t[d].l].size>=k) return kth(t[d].l,l,mid,k);
else return kth(t[d].r,mid+1,r,k-t[t[d].l].size);
}
LL query(int d,int l,int r,int w)
{
if (r<=w) return (LL)w*t[d].size-t[d].s;
if (l>=w) return t[d].s-(LL)w*t[d].size;
int mid=(l+r)/2;
return query(t[d].l,l,mid,w)+query(t[d].r,mid+1,r,w);
}
}t1,t2;
void solve1()
{
LL ans=0;
for (int i=1;i<=n;i++)
{
int x,y;
char ch1[2],ch2[2];
scanf("%s%d%s%d",&ch1,&x,&ch2,&y);
if (x>y) swap(x,y);
if (ch1[0]==ch2[0]) ans+=y-x;
else a[++a1]=x,a[++a1]=y,ans++;
}
sort(a+1,a+a1+1);
int mid=a[(a1+1)/2];
for (int i=1;i<=a1;i++) ans+=abs(a[i]-mid);
printf("%lld",ans);
}
bool cmp(data a,data b)
{
return a.val<b.val;
}
LL query()
{
int w1=t1.kth(t1.root,0,1000000000,(t1.t[t1.root].size+1)/2),w2=t2.kth(t2.root,0,1000000000,(t2.t[t2.root].size+1)/2);
return t1.query(t1.root,0,1000000000,w1)+t2.query(t2.root,0,1000000000,w2);
}
void solve2()
{
LL ans=0,mn=1e17;
for (int i=1;i<=n;i++)
{
char ch1[2],ch2[2];
scanf("%s%d%s%d",&ch1,&l[i],&ch2,&r[i]);
if (l[i]>r[i]) swap(l[i],r[i]);
if (ch1[0]==ch2[0]) ans+=r[i]-l[i],i--,n--;
else f[++f1].val=1.0*(l[i]+r[i])/2,f[f1].id=i,ans++;
}
sort(f+1,f+f1+1,cmp);
for (int i=1;i<=f1;i++)
{
t2.ins(t2.root,0,1000000000,l[f[i].id],1);
t2.ins(t2.root,0,1000000000,r[f[i].id],1);
}
mn=query()+ans;
for (int i=1;i<f1;i++)
{
t2.ins(t2.root,0,1000000000,l[f[i].id],-1);
t2.ins(t2.root,0,1000000000,r[f[i].id],-1);
t1.ins(t1.root,0,1000000000,l[f[i].id],1);
t1.ins(t1.root,0,1000000000,r[f[i].id],1);
mn=min(mn,query()+ans);
}
printf("%lld",mn);
}
int main()
{
scanf("%d%d",&m,&n);
if (m==1) solve1();
else solve2();
return 0;
}