【二分答案】【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)); } }