巴邻旁之桥

Description

一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B。
每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000。相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度。区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对。
城市中有 N 个居民。第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 K 座横跨河流的大桥。
由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。当政府建造最多 K 座桥之后,设 Di 表示第 i 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 D1+D2+?+DN 最小。

Input

输入的第一行包含两个正整数 K 和 N,分别表示桥的上限数量和居民的数量。
接下来 N 行,每一行包含四个参数:Pi,Si,Qi 和 Ti,表示第 i 个居民的房子在区域 Pi 的 Si 号建筑上,且他的办公室位于 Qi 区域的 Ti 号建筑上。

Output

输出仅为一行,包含一个整数,表示 D1+D2+?+DN 的最小值。

Sample Input

1 5
B 0 A 4
B 1 B 3
A 5 B 7
B 2 A 6
B 1 A 7
Sample Output

24
HINT

K=1或K=2
1≤N≤100000

题解

(家和单位在同一侧的提前算好)
当把所求的计算式列出之后,可以发现最优解是所有办公室和家的位置的中位数。

对于k=1的,中位数可以直接求出。

对于k=2的,可以发现:按照每个人的家和办公室的中点排序后,一定存在一个分割点使得前缀都走左边的桥,后缀都走右边的桥(因为走靠近中点的桥不会更差)。

于是我们枚举分割点,离散化后用权值线段树动态维护两个区间的中位数求解即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <vector>
#define pb push_back
#define M 300005
#define LL long long
using namespace std;
LL ans[M];
int size,n,k,cnt;
int ls[M],d[M];
char s1[10],s2[10];
struct data
{
    int x[3];
}a[M];
struct Segtree
{
    int size;
    LL sum;
}t[M<<2];
int z[10];
void lisan()
{
    sort(ls+1,ls+1+cnt);
    size=unique(ls+1,ls+1+cnt)-ls-1;
}
int Hash(int x)
{
    return lower_bound(ls+1,ls+1+size,x)-ls;
}
bool cmp(data a,data b)
{
    return a.x[1]+a.x[2]<b.x[1]+b.x[2];
}
void Update(int x)
{
    t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
    t[x].size=t[x<<1].size+t[x<<1|1].size;
}
void Build(int x,int l,int r)
{
    if (l==r)
    {
        t[x].sum=t[x].size=0;
        return;
    }
    int m=(l+r)>>1;
    Build(x<<1,l,m);
    Build(x<<1|1,m+1,r);
    Update(x);
}
void Insert(int x,int l,int r,int k)
{
    if (l==r)
    {
        t[x].sum+=d[l];
        t[x].size++;
        return;
    }
    int m=(l+r)>>1;
    if (k<=m) Insert(x<<1,l,m,k);
    else Insert(x<<1|1,m+1,r,k);
    Update(x);
}
LL Getsum(int x,int l,int r,int cnt)
{
    if (t[x].size<=cnt)
        return t[x].sum;
    if (l==r)
        return 1LL*cnt*d[l];
    int m=(l+r)>>1;
    if (t[x<<1].size>=cnt) return Getsum(x<<1,l,m,cnt);
    else return t[x<<1].sum+Getsum(x<<1|1,m+1,r,cnt-t[x<<1].size);
}
LL Query(LL k)
{
    LL s=Getsum(1,1,size,k);
    return t[1].sum-2LL*s;
}
int main()
{
    scanf("%d%d",&k,&n);
    LL pre=0;
    int tot=0;
    cnt=0;
    for (int i=1;i<=n;i++)
    {
        int x1,x2;
        scanf("%s%d%s%d",s1,&x1,s2,&x2);
        if (s1[0]==s2[0])
        {
            pre+=abs(x1-x2);
            continue;
        }
        pre++;
        a[++tot].x[1]=x1,a[tot].x[2]=x2;
        ls[++cnt]=x1,ls[++cnt]=x2;
    }
    if (cnt)
    {
        lisan();
        n=tot;
        for (int i=1;i<=n;i++)
            d[Hash(a[i].x[1])]=a[i].x[1],d[Hash(a[i].x[2])]=a[i].x[2];
        Build(1,1,size);
        sort(a+1,a+1+n,cmp);
        for (int i=1;i<=n;i++)
        {
            Insert(1,1,size,Hash(a[i].x[1]));
            Insert(1,1,size,Hash(a[i].x[2]));
            ans[i]=Query(i);
        }
    }
    if (k==1)
        cout<<ans[n]+pre<<endl;
    else
    {
        LL Ans=ans[n];
        if (size)
        {
            Build(1,1,size);
            for (int i=n;i>1;i--)
            {
                Insert(1,1,size,Hash(a[i].x[1]));
                Insert(1,1,size,Hash(a[i].x[2]));
                Ans=min(Ans,ans[i-1]+Query(n-i+1));
            }
        }
        cout<<Ans+pre<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值