Codeforces contest 883 problem L. Berland.Taxi(Treap+优先队列)

题目传送门:HERE

就让它成为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,愿你平安喜乐。

Codeforces contest 883 problem L. Berland.Taxi(Treap+优先队列)_第1张图片

你可能感兴趣的:(平衡树,非可持久化数据结构,STL)