题目
n(1<=n<=1e6)只蚂蚁,在一根杆上,杆左端点0,右端点1e9+1,
n只蚂蚁第i只的初始位置在ai,输入保证
di=0代表当前向左爬,di=1代表当前向右爬
左端点有一个柱子,被撞a(1<=a<=1e9)次后消失,被撞<=a次的时候都会把蚂蚁往反方向弹
右端点有一个柱子,被撞b(1<=b<=1e9)次后消失,被撞<=b次的时候都会把蚂蚁往反方向弹
思路来源
乱搞AC
题解
L为左右端点距离,考虑时间每过2*L是一个循环,
这时候左端点会被撞n次,右端点会被撞n次,所以左右端点可以先被撞min(a/n,b/n)个循环
后面的模拟即可,更新最后一次被撞的时间
以上是大多数人的写法,下面考虑我的乱搞
考虑二分(事实上500ms二分会T)a柱子是什么时候被撞烂的,b柱子是什么时候被撞烂的
在把a看成是先被撞烂的柱子时,所有撞到b的都会弹回来,
这样可以求得一个a被撞烂的时间,和一个b被撞烂的时间,
二者较小的那个是真实被撞烂的时间,
因为较小的被撞烂的时候,较大的一定没被撞烂,
所以在此之前的蚂蚁对撞较小的都有贡献,另一个则不一定是,
因为后续蚂蚁可能会在较小端掉下去,并不会对撞较大端有“只数的贡献”
不妨较小被撞烂的是左端点,被撞烂的时间是t1,
较大被撞烂的是右端点,被撞烂的时间是t2,
考虑右-左被撞烂的时间差,
现在撞烂左端点的这根柱子的这只蚂蚁,记为蚂蚁A,
会在L秒之后到达较大的这根柱子,且L秒内朝着较小的这根柱子走的蚂蚁都会掉下去,
L秒内朝着较大这根柱子走的蚂蚁,都在A的前面,所以考虑A什么时候掉下去,
①如果t2<t1+L,说明有一只蚂蚁B把右端点撞烂了,并向左端点走,
A走到右端点的时候会掉下去,这在B走到左端点的前面,答案是t2+L
而且能在t2撞烂右端点,说明在t2-L(t2-L<t1)的时间到达了左端点,
这时候左端点并没有烂,所以没有蚂蚁掉下去,所以撞烂右端点的时间是真实时间
②如果t2>t1+L,说明右端点不可能被撞烂,因为t1撞烂左端点后,
A蚂蚁都没能撞烂右端点,而在A蚂蚁之前撞右端点的一定会在左端点掉下去,不能再撞了
这就说明A是最后一个从左端点掉下去的蚂蚁,答案是t1+2*L
③如果t2=t1+L,可以认为是A蚂蚁把右端点撞烂的,也可以认为右端点没烂,
这不重要,无论哪种情况A蚂蚁还要返回,答案仍是t1+2*L
然后发现这个东西可以不二分,直接用下标求
然后实践发现这个距离数组排序会T,
所以利用序列的几乎有序性,多扫几遍序列来排序
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const ll mx=1e9+1,all=2ll*mx;
int n,a[N],d[N],x,y,l[N],r[N];
ll lef,rig,lb,rb;
int main(){
scanf("%d%lld%lld",&n,&lef,&rig);
lef--;rig--;
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
}
for(int i=0;i<n;++i){
scanf("%d",&d[i]);
if(d[i]==0)l[x++]=a[i];
}
for(int i=n-1;i>=0;--i){
if(d[i]==1)l[x++]=2ll*mx-a[i],r[y++]=mx-a[i];
}
for(int i=0;i<n;++i){
if(d[i]==0)r[y++]=a[i]+mx;
}
lb=l[lef%n]+1ll*(lef/n)*all;
rb=r[rig%n]+1ll*(rig/n)*all;
if(lb>rb)swap(lb,rb);
if(rb>=lb+mx)printf("%lld\n",lb+all);
else printf("%lld\n",rb+mx);
return 0;
}