2021上海省赛 H. 鸡哥的 AI 驾驶(二分答案)

只能说自己太菜了,真的完全想不到。

如果说单纯的想找到这个相撞的组合是十分困难的。所以全局的考虑一下,如果有两个车相撞,那么说明这两个车的相对位置发生了改变。而我们要找的肯定他超过与之最近的不同型号的左或右的车,因为如果他要超车,那么肯定是先超过第一个然后在第二个,那么我们的任务就变成了找到某个时刻第一次有两个不同型号的车的相对位置发生了改变。

那么我们就可以  二分答案   ,二分的标准即为时间。

我们可以先把每个车在它之前的第一个与他型号不同的车的编号与在他之后的第一个编号不同的记录在L,R数组里面,然后对于每一个t,验证在他左边的车是否晚于他出现,在他右边的车是否早于他出现。

其次对于找左右数组这里有一个巧法,这个更想不到,快哭了

    L[r[1].id]=-1,R[r[n].id]=-1;
    for(int i = 2; i <= n ; i++){
        if(r[i-1].p == r[i].p){
            L[r[i].id]=L[r[i-1].id];/如果型号相同,那他们两个的L数组相同即可
        }
        else{
            L[r[i].id]=r[i-1].id;/否则就等于他的下标
        }
    }
    for(int i = n-1; i >= 1; i--){
        if(r[i+1].p == r[i].p){
            R[r[i].id]=R[r[i+1].id];
        }
        else{
            R[r[i].id]=r[i+1].id;
        }
    }

然后就是check函数的选择

我们可以先把他们现在的位置按照大小排序,然后从前往后遍历,验证是否有他左边的车未出现,或者她右边的车出现了,但是这里可能有特殊情况,就是某个时刻他们到了同一个点,但是排序的话他们的相对位置并没有变。所以可以用一个vis存他是否出现,po数组存这个点的位置。

同时这里记录位置的数组po要开long long 或者用map存都可以,不然会爆

    for(int i = 1; i <= n ; i++){
        rr[i]=r[i];
        rr[i].x+=t*rr[i].v;
    }
    sort(rr+1,rr+1+n);
    for(int i = 1; i <= n ; i++){
        int pos1=L[rr[i].id],pos2=R[rr[i].id];
        if(pos1 != -1){/这样就不用考虑-1的情况了,因为考虑没有意义,他总是成立的
            if(!vis[pos1]){/如果压根不存在他们就说明超过了
                return true;
            }
            else{/如果在他之前,但他俩位置相同,还是超过了
                if(po[pos1] == rr[i].x)
                    return true;
            }
        }
        if(pos2 != -1){
            if(vis[pos2])
                return true;
        }
        vis[rr[i].id]=1;po[rr[i].id]=rr[i].x;
    }

然后对于不成立的情况,我们其实只要好到他的最大可能值如果这样都没有相撞,那么之后就更不会相撞了。极限为两个不同型号的车,一个位于-1e9,速度为1,一个位于1e9速度为0,那么他们共需要2e9的时间,所以设为3e9是绰绰有余,我设的习惯是维护一个端点,这里如果当前时刻是可行的,那么说明答案要么是当前时刻,要么比当前时刻早(因为这道题让我们找到最长的安全时刻)那么最总输出的就是相撞时刻-1

​
    ll l=0,r=3e9+10;
    while(l<r){
        ll mid=r+l-1>>1;
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    if(r>=3e9+10) printf("-1");
    else          printf("%d",r-1);

​

所以总的复杂度就是n*log(n)*log(n)

最后附上完整代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>

using namespace std;
typedef long long ll;
const int maxn = 1e5+10;

struct node{

    ll x,v,p,id;
    friend operator <(node x,node y){
        return x.x<y.x;
    }
}r[maxn],rr[maxn];
int n,m,a[maxn][2],L[maxn],R[maxn],vis[maxn],k1,k2;
map<int,ll>po;
bool check(ll t){

    memset(vis,0,sizeof vis);
    po.clear();
    for(int i = 1; i <= n ; i++){
        rr[i]=r[i];
        rr[i].x+=t*rr[i].v;
    }
    sort(rr+1,rr+1+n);
    for(int i = 1; i <= n ; i++){
        int pos1=L[rr[i].id],pos2=R[rr[i].id];
        if(pos1 != -1){
            if(!vis[pos1]){
                return true;
            }
            else{
                if(po[pos1] == rr[i].x)
                    return true;
            }
        }
        if(pos2 != -1){
            if(vis[pos2])
                return true;
        }
        vis[rr[i].id]=1;po[rr[i].id]=rr[i].x;
    }return false;

}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i = 1; i <= n ; i++){
        scanf("%lld %lld %lld",&r[i].x,&r[i].v,&r[i].p);
        r[i].id=i;
    }sort(r+1,r+1+n);
    L[r[1].id]=-1,R[r[n].id]=-1;
    for(int i = 2; i <= n ; i++){
        if(r[i-1].p == r[i].p){
            L[r[i].id]=L[r[i-1].id];
        }
        else{
            L[r[i].id]=r[i-1].id;
        }
    }
    for(int i = n-1; i >= 1; i--){
        if(r[i+1].p == r[i].p){
            R[r[i].id]=R[r[i+1].id];
        }
        else{
            R[r[i].id]=r[i+1].id;
        }
    }
    if(m == 1){/这样可以直接判定不可能
        printf("-1");return 0;
    }
    ll l=0,r=3e9+10;
    while(l<r){
        ll mid=r+l-1>>1;
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    if(r>=3e9+10) printf("-1");
    else          printf("%d",r-1);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值