CF160E Buses and People

题意

有一群人要坐公交车。
人的路线和车的看路线在同一个数轴上。
i i i要从 l i l_i li r i r_i ri并在时间 b i b_i bi开始在 l i l_i li这个点等车。
i i i s i s_i si开到 f i f_i fi并在时间 t i t_i ti s i s_i si出发。
车开得很快,相当于瞬时到达且停靠每一个经过的点。
人只能上来得不早于他的车且不能换车。

思路

首先题目给的条件可以知道:

  1. 只有车行的路程完全包括人行的路程且车来得不比人早,也就是 s i ≤ l j & & f i ≥ r j & & t i ≥ b j s_i\le l_j \&\& f_i \ge r_j \&\& t_i \ge b_j silj&&firj&&tibj时,人 j j j才能乘坐车 i i i
  2. 人肯定坐得是最早且不比他早到的那辆车。

显然可以用 Θ ( n 2 ) \Theta(n^2) Θ(n2)爆扫求出答案,但也显然会T。
所以需要一种更优的解法。
由于考虑到有左端点,右端点,时间三维,你可以用高级数据结构(平衡树),或是偏序做此题(可惜我都不会)。于是只能用线段树加一些操作卡过去。

解法

先将所有的时间离散化(这样可以用作线段树的下标),然后将所有左端点排序(排掉一维)。然后按左端点从小到大枚举(相同时车在前),如果是车,将车的 i d id id和右端点插入线段树(线段树里记录某个时间段能达到的最右端点和对应的车的id),如果是人,就在线段树中查询最优的车。这样可以达到 Θ ( n l o g ( n ) ) \Theta(n log(n)) Θ(nlog(n))

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const int Mx=1e5+5;
#define FST youxiaokelianyouwuzhu

struct Buses{
    LL l,r,t,id;
    #define B buses
}buses[Mx];
struct Person{
    LL l,r,t,id;
    #define P person
}person[Mx];
LL ans[Mx],t[Mx<<1];
int n,m;

class SegTree{
public:
    #define s segnode
    #define I inline
    #define ls k<<1
    #define rs k<<1|1
    I void buildSegTree(int k,int l,int r){
        s[k].l=l;s[k].r=r;
        if(l==r)return;
        int mid=(l+r)>>1;
        buildSegTree(ls,l,mid);
        buildSegTree(rs,mid+1,r);
    }
    I void opi(int i){
        int id=lower_bound(t+1,t+n+m+1,B[i].t)-t;
        insert(1,id,B[i].id,B[i].r);
    }
    I void ops(int i){
        int id=lower_bound(t+1,t+n+m+1,P[i].t)-t;
        ans[P[i].id]=search(1,id,P[i].r);
    }
private:
    struct SegNode{
        int l,r,id;
        LL v;
    }segnode[Mx<<3];
    void pushUp(int k){
        s[k].v=max(s[ls].v,s[rs].v);
    }
    I void insert(int k,int pos,int id,LL v){
        int sl=s[k].l,sr=s[k].r;
        if(sl==sr){s[k].v=v;s[k].id=id;return;}
        int mid=(sl+sr)>>1;
        if(pos<=mid)insert(ls,pos,id,v);
        if(pos>mid)insert(rs,pos,id,v);
        pushUp(k);
    }
    I int search(int k,int pos,LL v){
        int sl=s[k].l,sr=s[k].r;
        if(s[k].v<v)return -1;
        if(s[k].r<pos)return -1;
        if(sl==sr)return s[k].id;
        int mid=(sl+sr)>>1;
        int res=search(ls,pos,v);
        return res==-1?search(rs,pos,v):res;
    }
    #undef s
    #undef I
    #undef ls
    #undef rs
};SegTree T;

template<class T> 
bool cmp(T a,T b){return a.l<b.l;};

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&B[i].l,&B[i].r,&B[i].t),B[i].id=i;
    for(int i=1;i<=m;i++)scanf("%lld%lld%lld",&P[i].l,&P[i].r,&P[i].t),P[i].id=i;
    T.buildSegTree(1,1,n+m);
    for(int i=1;i<=n;i++)t[i]=B[i].t;
    for(int i=n+1;i<=n+m;i++)t[i]=P[i-n].t;
    sort(t+1,t+1+n+m);
    sort(B+1,B+1+n,cmp<Buses>);
    sort(P+1,P+1+m,cmp<Person>);
    int bi=1,pi=1;
    while(bi<=n||pi<=m){
        if(pi>m)break;
        if(bi>n||B[bi].l>P[pi].l)T.ops(pi),pi++;
        else T.opi(bi),bi++;
    }
    for(int i=1;i<=m;i++)printf("%lld ",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值