Buses and People + CodeForces - 160E

Buses and People 三维处理

题目大意:有n辆公交车,每辆公交车有s(起始点),f(终点),t(发车时间) ,有m个人,每个人有l(起点),r(终点),t(出现时间)
对于一个人,当t(人)<=t(车), s<=l,r <=f 时就能上这辆车,问能上的发车时间最早的是那一辆

类似三维偏序问题,有三个维度需要我们解决。首先,车和人都有l,r,t,三个属性,先按照l排序,解决第一维,这样,对于一个人,前面的所有的车都是可能上去的。剩下的两个维度用线段树来解决,线段树维护区间最大值。线段树下标对应的是t,维护的最大值是r。把车和人在一起排序,从头遍历,是车,就在t位置插入r,是人,就在线段树t位置到末端查询满足小于r的最小的下标对应的id(也就是车的id)。

怎么也没想到可以用一个线段树直接解决两维。
查询到一个人,前面已经插入的车的l一定都比它小了;线段树查询的是t到末端,t大于人的车一定在其中;线段树维护的是r最大值,只要查询的区间内的r有大于等于此人的r,那么这个人就一定能上车。

排序解决一维,线段树解决一维,维护的最大值解决一维。

#include <cstdio>
#include <algorithm>
#include <cmath>
#define Mid ((a[k].l+a[k].r)>>1)
#define cl (k<<1)
#define cr (k<<1|1)
#define Max 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int maxn=2e5+5;

struct Q{
    int l,r,t, id,tp;//tp==0 车, tp==1 人
    friend bool operator <(Q a, Q b){
        if(a.l!=b.l) return a.l < b.l;	//按照l排序,若l相同,人在后
        return a.tp < b.tp;
    }
}q[maxn*2];
struct N{
    int l,r;
    int ma,id;
}a[maxn*4];
int dis[maxn*2], cur;//离散化
int ans[maxn];

void build(int k,int l,int r){
    a[k].l=l, a[k].r=r;
    a[k].ma = -1;
    a[k].id = -1;
    if(l==r) return;
    int mid=Mid;
    build(cl,l,mid), build(cr,mid+1,r);
}
void updata(int k,int i,int x, int id){
    if(a[k].l==a[k].r){
        if(a[k].ma < x){
            a[k].ma = x;
            a[k].id = id;
        }
        return;
    }
    int mid=Mid;
    if(i<=mid) updata(cl,i,x,id);
    else       updata(cr,i,x,id);

    if(a[k].ma < a[cl].ma){
        a[k].ma = a[cl].ma;
        a[k].id = a[cl].id;
    }
    if(a[k].ma < a[cr].ma){
        a[k].ma = a[cr].ma;
        a[k].id = a[cr].id;
    }
}
int get(int k,int l,int r,int x){//返回符合要求的最小的下标 对应的id

    if(a[k].l==a[k].r && a[k].ma>=x)
        return a[k].id;

    int mid=Mid;
    int ret=Max;
    if(l<=mid && a[cl].ma>=x ){	//若左边有符合的,可以直接返回,左边的下标(t)一定比右边小
        ret = get(cl,l,r,x);
        if(ret!=Max) return ret;
    }
    if(r>mid && a[cr].ma>=x ){
        ret = get(cr,l,r,x);
        if(ret!=Max) return ret;
    }
    return ret;
}
int gs(int x){return lower_bound(dis+1,dis+1+cur,x)-dis; }//离散化

int main()
{
    int n,m,qn=0;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++){
        qn++;
        scanf("%d%d%d",&q[qn].l, &q[qn].r, &q[qn].t);
        q[qn].tp = 0;
        q[qn].id = i;
        dis[++cur] = q[qn].t;
    }
    for(int i=1; i<=m; i++){
        qn++;
        scanf("%d%d%d",&q[qn].l, &q[qn].r, &q[qn].t);
        q[qn].tp = 1;
        q[qn].id = i;
        dis[++cur] = q[qn].t;
    }

    sort(dis+1,dis+cur+1);
    cur = unique(dis+1,dis+cur+1) - (dis+1);

    sort(q+1,q+1+qn);
    build(1,1,cur);

    for(int i=1; i<=qn; i++){
        if(q[i].tp==0){//车
            updata(1, gs(q[i].t) ,q[i].r, q[i].id);
        }else{
            int x=get(1, gs(q[i].t), cur, q[i].r);
            if(x==Max)//没找到
                ans[q[i].id] = -1;
            else
                ans[q[i].id] = x;
        }
    }
    for(int i=1; i<=m; i++){
        printf("%d ",ans[i]);
    }
    printf("\n");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值