Description
在高速公路上(0<=M<=1e9),路旁有N(N <= 100000)头奶牛,他们的出发地和目的地分别是Xi,Yi。
你从高速公路的起点0出发,每次只能载一头奶牛,要把所有奶牛送到目的地,并且最终要到高速公路的尽头M,求最短的路程和。
PS.为了最短,我们可以在中途抛下某头奶牛,只要最后每头奶牛都到目的地就行。
Analysis
这题有两个要求:
1、所有奶牛到目的地。
2、最终要到尽头。
注意
Xi
和
Yi
是无序的,既可以有
(1,2)
,也可以有
(7,2)
首先这个
M
太大,我们先离散化。
得到
如果
Xi<Yi
,那我们不难想到这样的流程:
从0开始走,每一步都把当前所有奶牛带上(由于一次只能带1个所以往返多次带走),带到下一个位置。
如图:
我们可以先开车开到a接上牛A。
然后载着牛A去b接牛B。
由于只能载一头牛,所以我们要在b–>c之间走3趟,去一次,回来,再去一次。
最后载着牛b去d点,牛A已经在c到站了。
这一定是最优的,因为我们已经尽可能地少走每一段路了,要走的都是必须走的。
但是题目可能存在 Xi>Yi
对于
Xi>Yi
我们可以理解为搭了顺风车,【我们在
Xi<Yi
的时候返程是没有载人的】,如果能搭顺风车那么这对
Xi,Yi
就是免费的。
到这里就是我比赛时候的思路了。
我打了一个堆来维护
Xi<Yi
有至少要多少路程,同时统计顺风车的数量。
Xi>Yi
能搭顺风车就搭不能就多跑一段路。
其实这个已经无限接近正解了。
但有些特殊情况没有处理到。
对于绿色的牛,是可以搭顺风车回去的。
但是对于粉色的牛,没有顺风车可以搭,只能单独接他到目的地。
由于我们讨论的顺风车搭不上于是就多跑一段路,导致d到e没有被算上。然后就只有60分了。
事后我也加了点特判但是无济于事。我们需要换下想法。
其实对于回来的点也是一样的嘛,如果我们把出发点设为M的话。
我们用
Prei
记录
i
位置被多少对
Sufi
记录
i
位置被多少对
那么对于
i
位置到
i
这个位置有
i+1
这个位置有
Sufi+1
头牛要到
i
如果
如果
Prei<=Sufi+1
,那么要跑这段路
Sufi+1∗2+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;
}