【JZOJ】3187 的士

来源:互联网 发布:寐蚕丝被 知乎 编辑:程序博客网 时间:2024/04/28 13:28

Description

在高速公路上(0<=M<=1e9),路旁有N(N <= 100000)头奶牛,他们的出发地和目的地分别是Xi,Yi。你从高速公路的起点0出发,每次只能载一头奶牛,要把所有奶牛送到目的地,并且最终要到高速公路的尽头M,求最短的路程和。PS.为了最短,我们可以在中途抛下某头奶牛,只要最后每头奶牛都到目的地就行。

Analysis

这题有两个要求:
1、所有奶牛到目的地。
2、最终要到尽头。
注意XiYi是无序的,既可以有(1,2),也可以有(7,2)

首先这个M太大,我们先离散化。
得到2N个位置,此时位置与位置之间的距离就不是1了而是离散化的距离了。
如果Xi<Yi,那我们不难想到这样的流程:
从0开始走,每一步都把当前所有奶牛带上(由于一次只能带1个所以往返多次带走),带到下一个位置。
如图:
这里写图片描述
我们可以先开车开到a接上牛A。
然后载着牛A去b接牛B。
由于只能载一头牛,所以我们要在b–>c之间走3趟,去一次,回来,再去一次。
最后载着牛b去d点,牛A已经在c到站了。

这一定是最优的,因为我们已经尽可能地少走每一段路了,要走的都是必须走的。

但是题目可能存在Xi>Yi

对于Xi>Yi我们可以理解为搭了顺风车,【我们在Xi<Yi的时候返程是没有载人的】,如果能搭顺风车那么这对XiYi就是免费的。
到这里就是我比赛时候的思路了。
我打了一个堆来维护Xi<Yi有至少要多少路程,同时统计顺风车的数量。Xi>Yi能搭顺风车就搭不能就多跑一段路。

其实这个已经无限接近正解了。
但有些特殊情况没有处理到。这里写图片描述
对于绿色的牛,是可以搭顺风车回去的。
但是对于粉色的牛,没有顺风车可以搭,只能单独接他到目的地。
由于我们讨论的顺风车搭不上于是就多跑一段路,导致d到e没有被算上。然后就只有60分了。
事后我也加了点特判但是无济于事。我们需要换下想法。

其实对于回来的点也是一样的嘛,如果我们把出发点设为M的话。
我们用Prei记录i位置被多少对Xi<Yi跨越
Sufi记录i位置被多少对Xi>Yi跨越。
那么对于i位置到i+1位置的路径应该走多少遍呢?
i这个位置有Prei头牛要到i+1
i+1这个位置有Sufi+1头牛要到i
如果Prei>Sufi+1,那么要跑这段路Prei21趟。
如果Prei<=Sufi+1,那么要跑这段路Sufi+12+1趟。
对于0的特殊情况讨论一下【其实这样已经不用特殊讨论了,但是最好讨论一下加深一下理解】
Prei,Sufi,我们可以通过打标记的前缀和、树状数组、堆等各种算法弄出来。
最后别忘了我们从0出发到M去,把这两个位置加入离散化然后一起讨论就好了。

不懂的看看代码吧。

#include<cstring>#include<cstdio>#include<algorithm>#include<map>#include<queue>#include<vector>using namespace std;typedef long long LL;const int N = 101000;priority_queue<int , vector<int> , greater<int> > Q;priority_queue<int> Q2;map<int , int> Hash;struct Data{    int x,y;}a[N],b[N];int c[N * 2],id,tot,tot2,td,n,m,mxt;LL P[N * 2],ans,sum[N * 2],sum2[N * 2];bool cmp(Data x , Data y) { return x.x < y.x;}bool cmp2(Data x , Data y) { return x.x > y.x;}void Relable(){    sort(c , c + 1 + td);    for (int i = 0 ; i <= td ; i ++)    {        int p = Hash[c[i]];        if (!p) Hash[c[i]] = ++ id , P[id] = c[i]; else continue;    }    for (int i = 1 ; i <= tot ; i ++) a[i].x = Hash[a[i].x] , a[i].y = Hash[a[i].y];    for (int i = 1 ; i <= tot2 ; i ++) b[i].x = Hash[b[i].x] , b[i].y = Hash[b[i].y];}void Solve(){    int pos = a[1].x , l = 1;    while (a[l].x == pos && l <= tot) Q.push(a[l].y) , ++ l;    while (pos <= id)    {        sum[pos] = Q.size();        pos ++;        while (a[l].x == pos && l <= tot) Q.push(a[l ++].y);        int p = Q.top();        while (Q.top() <= pos && !Q.empty()) Q.pop();    }    pos = b[1].x , l = 1;    while (b[l].x == pos && l <= tot2) Q2.push(b[l ++].y);    while (pos)    {        sum2[pos] = Q2.size();        pos --;        while (b[l].x == pos && l <= tot2) Q2.push(b[l ++].y);        while (Q2.top() >= pos && !Q2.empty()) Q2.pop();    } //此处可以好好恶搞= = 我打了堆比较丑     //sum就是Pre,sum2就是Suf     //此处可以不用讨论= =     for (int i = 1 ; i < id ; i ++)         if (sum[i] && sum2[i + 1])        {            if (sum[i] > sum2[i + 1]) ans += (sum[i] * 2 - 1) * (P[i + 1] - P[i]);                else ans += (sum2[i + 1] * 2 + 1) * (P[i + 1] - P[i]);        } else {            if (!sum2[i + 1] && sum[i]) ans += (sum[i] * 2 - 1) * (P[i + 1] - P[i]);            if (sum2[i + 1] && !sum[i]) ans += (sum2[i + 1] * 2 + 1) * (P[i + 1] - P[i]);            if (!sum2[i + 1] && !sum[i]) ans += (P[i + 1] - P[i]);        }}int main(){    freopen("1.in" , "r" , stdin);freopen("1.out" , "w" , stdout);    scanf("%d%d" , &n , &m);    c[0] = 0;    for (int i = 1 ; i <= n ; i ++)    {        int x,y;scanf("%d%d" , &x , &y);        if (x < y && x != y) a[++ tot].x = x , a[tot].y = y , mxt = max(mxt , y);            else if (x != y) b[++ tot2].x = x , b[tot2].y = y , mxt = max(mxt , x);        if (x != y) c[++ td] = x , c[++ td] = y;    }    c[++ td] = m;    Relable();//离散化     sort(a + 1 , a + 1 + tot , cmp);sort(b + 1 , b + 1 + tot2 , cmp2);    Solve();    printf("%I64d\n" , ans);    return 0;}
0 0