【GDOI2018模拟8.7】的士碰撞

14 篇文章 0 订阅

Description

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

Input

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

Output

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

Sample Input

输入1:
5 5
1 1
4 1
2 0
7 1
11 0
5 1
10 2
7 3
8 4
20 5

输入2:
20 15
31116973 1
721410312 0
152891538 1
55434456 0
903968 1
34492580 0
97565125 0
78559065 1
191708700 0
335941230 0
526621966 1
168159348 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
625365533 5
668115287 6
93821572 7
175176488 5
438184710 1
71279702 12

Data Constraint

n,q<=100000
0<=a[i]<=109
t<=109

Solution

这题真的太妙了
两个车碰撞,相当于两个车互相穿过,编号互换
而且无论怎么碰撞,车辆之间的相对位置是不会改变的
比如说1号车在2号车左边
那么2号车无论怎么撞都不可能跑到1号车左边
那么t时刻后每辆车的位置就是a[i]+/-t
这时给出要求的车的编号,我们知道这个编号的车初始时的排名,设为y
那么答案就是所有车中排名为y的那辆车的坐标


这个二分坐标mid
那么就是要求有多少车在这个坐标前面
将车往左和往右的分为两种
在往左的车中二分求出初始位置为mid+t之前的有多少
往右的车中二分求出初始位置为mid-t之前有多少
就可以得出答案了

PS
二分有个upper_bound和lower_bound
upper_bound是求出>k的第一个位置
lower_bound是求出>=k的第一个位置
这题中第二个二分一个简单方法就是upper_bound出位置之后-1

Code

#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 101000
#define INF 10000000000ll
#define ll long long
using namespace std;
ll n,m,b[N],c[N],ans[N],t1=0,t2=0,d[N];
struct node{
    ll x,y,z;
}a[N];
bool cnt(node x,node y){return x.x<y.x;}
int main()
{
    freopen("collision.in","r",stdin);
    freopen("collision.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%lld%lld",&a[i].x,&a[i].y),a[i].x+=INF,a[i].z=i;
    sort(a+1,a+n+1,cnt);
    fo(i,1,n)
    {
        d[a[i].z]=i;
        if(a[i].y==0) c[++t2]=a[i].x;
        else b[++t1]=a[i].x;
    }
    b[++t1]=INF+INF+INF+2;
    c[++t2]=INF+INF+INF+2;
    fo(i,1,m)
    {
        ll x,y;scanf("%lld%d",&x,&y);
        ll l=0,r=INF+INF+INF+INF;
        while(l+1<r)
        {
            ll mid=(l+r)/2,ans=0;
            ll z=upper_bound(b+1,b+t1+1,mid-x)-b-1;
            z=z+upper_bound(c+1,c+t2+1,mid+x)-c-1;
            if(z<d[y]) l=mid;else r=mid;
        }
        printf("%lld\n",abs(r-INF));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值