的士

题意

Bessie为农场上的其他奶牛提供的士服务。奶牛们在一条长为M(1<=M<=1,000,000,000)的栅栏的不同位置上。不幸的是,它们厌倦了它们现在所在的位置而想要去栅栏上其他的位置。Bessie必须把她每一个朋友从它们各自的起始地接上车然后送它们到目的地。但Bessie的车太小了,所以她每次只能运送一只奶牛。奶牛们上车下车是瞬间的事情。
为了省油钱,Bessie想要使她的驾驶量最小。给出N只奶牛(1<=N<=100,000)每一只的起始地和目的地,计算Bessie最少需要的驾驶量。Bessie意识到她需要偶尔把牛放在某一个地方而不是把它送到目的地才能省油钱。
Bessie在栅栏最左端(位置0)开始工作,而且必须在最右端(位置M)结束她的工作。

分析

很明显,在过程中,我们不会把一个奶牛带到离它目的地更远的地方,若要在某个地方回头走,我们可以把奶牛放在当前位置。
于是将原来的关键点排序后,每两个相邻的点构成了一条线段。对于一条线段,我们考虑路径要经过这条线段的路线。记A为起点在线段左端点及其以左的位置的路线数,B为起点在线段右端点及其以右的位置的路线数。我们是从左往右走。
A>B ,我们就要经过这条线段 2A1) 次,才能把之前放在线段左端点的奶牛运到线段右端点(并在从线段右端点回来的同时带上要来左端点的奶牛)
AB ,我们要经过这条线段 2B+1 次才能满足条件。
这样,我们能求出每条线段对答案的贡献,这样得出的答案一定是最优的。因为在局部你的做法是最优的,在全局你一定得出的是最优解。

#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;

typedef long long LL;
const int N = 1e5 + 10;
int a[N * 2];
map<int,int> num;
int nt,n,m;
int pre[N * 2],suf[N * 2],e[N * 2];
int s[N][2];

int main() { 
    scanf("%d%d",&n,&m);
    int tot = 0,last = 0;
    for (int i = 1;i <= n;i ++) {
        scanf("%d%d",&s[i][0],&s[i][1]);
        a[++ tot] = s[i][0];
        a[++ tot] = s[i][1];
    }
    sort(a + 1,a + 1 + tot);
    for (int i = 1;i <= tot;i ++) if (i == 1 || a[i] != a[i - 1]) {
        num[a[i]] = ++ nt;
        e[nt] = a[i];
    }
    for (int i = 1;i <= n;i ++) {
        int x = num[s[i][0]],y = num[s[i][1]];
        if (x < y) pre[x] ++,pre[y] --;
        else suf[x] ++,suf[y] --;
    }
    for (int i = 1;i <= nt;i ++) pre[i] += pre[i - 1];
    for (int i = nt;i;i --) suf[i] += suf[i + 1];
    LL ans = e[1] + m - e[nt];
    for (int i = 1;i < nt;i ++) {
        int A = pre[i],B = suf[i + 1];
        if (A > B) {
            ans += LL(2 * A - 1) * LL(e[i + 1] - e[i]);
        }
        else ans += LL(2 * B + 1) * LL(e[i + 1] - e[i]);
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值