【二分答案】【NOIP模拟10-21】的士碰撞

Description

 ?辆车在一条数轴上,车的编号为1到?。编号为?的车坐标为?[?],初始方 向为???[?](左或右),初始位置两两不同。每辆车每个时刻行走距离为1。两辆 车相碰时,会调转方向,继续行走,掉头不消耗时间。现在车子开始朝其方向行 驶,同一个坐标允许有多辆车。现在有?个询问,给出?, ?,询问过了?时刻后, 编号为?的车的坐标的绝对值。 

Input Format

输入文件名为collision.in。 首先输入?, ?。 接下来?行,每行两个整数?[?], ???[?],若???[?] = 0,表示车子向左行走,若 ???[?] = 1,表示车子向右行走。 接下来?行,每行两个整数?, ?,询问时刻?时编号为?的车的坐标。 

Output Format

输出文件名为collision.out。 对于每个询问,输出一个整数,代表编号为?的车的坐标。

Sample Input & Sample Output

【输入输出样例1】

collision.in    5 5 1 1 4 1 2 0 7 1 11 0 5 1 10 2 7 3 8 4 20

collision.out  5 3 1 8 12 27

【输入输出样例2】

collision.in

 20 15 31116973 1 721410312 0 152891538 1 55434456 0 903968 1 34492580 0 97565125 0 78559065 1 191708700 0 335941230 0 526621966 1 25622049 38331852 977130985 662422758 171848754 220003868 5474029 533404717 11069472 1056101384 524968026 326917138168159348 1 457798506 1 160026937 1 76511872 1 247171016 1 48722268 0 159552820 0 701333640 0 434868520 1 143857480 13 821356724 11 132436670 1 20249229 11 504666 16 138701034 19 339607872 1 184664000 13 80827802 15 

collision.out 

625365533 5 668115287 6 93821572 7 175176488 5 438184710 1 71279702 12 237940668 420617936 689224277

Hint

【数据规模与约定】 对于30%的数据,max(?) ×? ≤ 107 另外有30%的数据,?, ? ≤ 1000 对于100%的数据,?, ? ≤ 100000,0 ≤ ?[?] ≤ 109 ,? ≤ 109 , ???[?] ∈ {0,1}

Solution

首先的士碰撞之后会掉头,
在这里我们可以理解为两辆车互换了编号,
然后继续前行。
那如何求出编号为x的最终位置呢?
这里可以用二分答案,
要用二分,我们需要单调。
因为在坐标轴上,从小到大,很显然是单调的。
如何验证答案,


如图,我们可以发现箭头变大了一辆车的rank是不会改变的,
因为大家的速度是一致的,不存在追及,而且相遇后会掉头,
所以它会一直在原来位置的前一辆车和后一辆车中间。
我们把车按照位置排一次序,
然后我们二分一个位置,使这个位置的rank与原来的相等;
询问这个位置的rank直接for循环一遍会超时,
所以我们继续二分,先按照方向和位置,把车分成A、B两组,
分别表示向右走和向左走的车的原始位置。
然后分别二分找出A中与B中比X小的车数量,两者相加就是X的rank,即可验证答案.

 

#include<cstdio>
#include<algorithm>
#define LL long long

int n,q,tota,totb;
LL a[100005],b[100005];
struct node
{
    int pos,rank,num;
}nod[100005];

bool cmp(node x,node y)
{
    return x.pos<y.pos;
}

int Count(LL a[],int len,LL x)
{
    int res=0,l=1,r=len;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (a[mid]<=x) res=mid,l=mid+1;
        else r=mid-1;
    }
    return res;
}

int main()
{
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++)
    {
        int dir;
        scanf("%d%d",&nod[i].pos,&dir);
        nod[i].num=i;
        if (dir) a[++tota]=nod[i].pos;
        else b[++totb]=nod[i].pos;
    }
    std::sort(nod+1,nod+n+1,cmp);
    for (int i=1;i<=n;i++) nod[nod[i].num].rank=i;    
    std::sort(a+1,a+tota+1);
    std::sort(b+1,b+totb+1);
    for (int i=1;i<=q;i++)
    {
        int tim,num,ans=0;
        scanf("%d%d",&tim,&num);
        num=nod[num].rank;
        LL l=std::min(a[1]+tim,b[1]-tim),r=std::max(a[tota]+tim,b[totb]-tim);
        while (l<=r)
        {
            LL mid=(l+r)>>1;
            int count=Count(a,tota,mid-tim)+Count(b,totb,mid+tim);
            if (count>=num) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d\n",abs(ans));
    }
}

 

转载于:https://www.cnblogs.com/Shawn7xc/p/7707449.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值