就让它成为2017年的最后一篇博客吧
这道无脑数据结构题
题面过长,翻译自寻。
这是一道令人愉悦的巧妙的数据结构题。
这道题,从爆零到AC用了我两天的时间。
直接分析题目。非常明显,我们需要一些数据结构去处理车辆的调动。用堆来存储那些载客的车,记录那些正在载客车辆的编号与可调用的时刻。
然后我们对于每一单,需要call那个离出发地最近的taxi,这点用线段树或者Treap可以解决。但需要注意的是,每个位置可能有多辆可用的车!但是一棵线段树的叶子节点怎么放得下呢?嵌套多一重结构?所以最好用平衡树。因为平衡树只要都塞进去就行了。
我从线段树很快地改成了Treap后(真的很快),不断地各种WA。然后发现我的Treap根本没写错,一个小细节就是乘客要等车时有多辆车最先完成那里出了问题。我直接把堆顶拿去用了,实际我应该把时间相同的拿出来丢进Treap里再找,然后累计等待时间。而且,这样做可能会使Treap里的车的可用时刻超过后面人的叫车时刻,于是还要判断一下。
至于具体如何实现,请看这里:
①一开始将所有车插入进Treap中,Treap里有三个关键字,位置,可用的时刻,编号。对于每一单直接查询当前位置对于第一关键字的前驱和后继,然后我们再找前驱的位置的后继作为真正的前驱(随便想一下)。然后选择较优的输出并将该车从Treap中删去丢进堆里,记录下次其可用的时刻和位置。
②由于时间升序,每单前将堆里的可用车弹出来丢进Treap中。如果Treap仍旧为空,将可用时刻和堆顶相同的全部弹出来丢进Treap中(这里我的操作很迷)。堆里的关键字就是车的可用时刻的升序。这里有一个厉害的重载:一开始我想要重载>(因为STL自带大根堆),发现不行,于是我改成了反向重载小于号。例如如果你想排升序,就把小于号改成降序之类的。
③Coderforces输出64位整数是%I64d。
④剩下一堆细节参看代码(有详细注释)。
时间复杂度:O(nlogn)。
#include //万能头文件
#define maxn 200010
using namespace std;
typedef long long LL;
int n, k, m, cnt;
int X[maxn];
struct Pair{
int id;
LL tim;
Pair(int _ = 0, LL __ = 0LL){
id = _;
tim = __;
}
bool operator < (const Pair& WT) const{
return tim > WT.tim;
}//重载
};
priority_queue Q;
struct Treap{
int fix, id, pos;
Treap *L, *R;
LL tim;
}Node[maxn<<1], *Root;
bool Check(Treap *p, int id, LL tim, int pos){
if(pos < p->pos) return true;
if(pos == p->pos && tim < p->tim) return true;
if(pos == p->pos && tim == p->tim && id < p->id) return true;
return false;
}
Treap *NewTnode(int id, LL tim, int pos){
Node[cnt].L = Node[cnt].R = NULL;
Node[cnt].id = id;
Node[cnt].pos = pos;
Node[cnt].tim = tim;
Node[cnt].fix = rand();
return Node+cnt++;
}
void LRotate(Treap *&a){
Treap *b = a->R;
a->R = b->L;
b->L = a;
a = b;
}
void RRotate(Treap *&a){
Treap *b = a->L;
a->L = b->R;
b->R = a;
a = b;
}
void TreapInsert(Treap *&p, int id, LL tim, int pos){
if(!p) p = NewTnode(id, tim, pos);
else if(Check(p, id, tim, pos)){
TreapInsert(p->L, id, tim, pos);
if(p->L->fix < p->fix) RRotate(p);
}
else{
TreapInsert(p->R, id, tim, pos);
if(p->R->fix < p->fix) LRotate(p);
}
}
void TreapDel(Treap *&p, Treap *x){
if(x == p){
if(!p->L || !p->R){
if(p->L) p = p->L;
else p = p->R;
}
else if(p->L->fix < p->R->fix){
RRotate(p);
TreapDel(p->R, x);
}
else{
LRotate(p);
TreapDel(p->L, x);
}
}
else if(Check(p, x->id, x->tim, x->pos)) TreapDel(p->L, x);
else TreapDel(p->R, x);
}
Treap *FindPre(Treap *p, int pos, Treap *now){
if(!p) return now;
if(p->pos <= pos) return FindPre(p->R, pos, p);
else return FindPre(p->L, pos, now);
}
Treap *FindSuc(Treap *p, int pos, Treap *now){
if(!p) return now;
if(p->pos >= pos) return FindSuc(p->L, pos, p);
else return FindSuc(p->R, pos, now);
}
void Take(int id, LL tim, int pos, LL tt, int aa, int bb){
tim += (LL)abs(pos - aa);
printf("%d %lld\n", id, tim);
Q.push(Pair(id, tt+tim+(LL)abs(aa-bb)));
X[id] = bb;
}
int main(){
scanf("%d%d%d", &n, &k, &m);
for(int i = 1; i <= k; i++) scanf("%d", &X[i]);
for(int i = 1; i <= k; i++) TreapInsert(Root, i, 0LL, X[i]);
LL tt;
int aa, bb;
for(int i = 1; i <= m; i++){
scanf("%lld%d%d", &tt, &aa, &bb);
LL Lim = tt;
if(!Root) Lim = max(tt, Q.top().tim);//很重要的细节
while(!Q.empty()){
Pair temp = Q.top();
if(temp.tim > Lim) break;
TreapInsert(Root, temp.id, temp.tim, X[temp.id]);
Q.pop();
}
Treap *Left = FindPre(Root, aa, NULL);
Treap *Right = FindSuc(Root, aa, NULL);
if(Left) Left = FindSuc(Root, Left->pos, NULL);
if(Left && !Right){
Take(Left->id, max(0LL, Left->tim-tt), Left->pos, tt, aa, bb);//等待时间为max(0LL, Left->tim-tt)
TreapDel(Root, Left);
}
else if(Right && !Left){
Take(Right->id, max(0LL, Right->tim-tt), Right->pos, tt, aa, bb);
TreapDel(Root, Right);
}
else{
int d1 = abs(Left->pos - aa), d2 = abs(Right->pos - aa);
if(d1 > d2) swap(Left, Right);
else if(d1 == d2 && Left->tim > Right->tim) swap(Left, Right);
else if(d1 == d2 && Left->tim == Right->tim && Left->id > Right->id) swap(Left, Right);
Take(Left->id, max(0LL, Left->tim-tt), Left->pos, tt, aa, bb);
TreapDel(Root, Left);
}
}
return 0;
}
2018,愿你平安喜乐。