题目链接
二分答案,对时间进行二分,假设每得到的时间是x,那么就以该时间来判断是否可以,满足条件
判断方法:
对于时间x,指定的某个人算出这个人要跑到终点,那么炸弹应该放置的区间,这样把所有向左跑的人区间算出来,然后同样的算出向右的人的区间,如果这些区间有整点重合(因为炸弹只能放在整点),那么就是满足的二分的右区间减小,反之亦然。
这里处理区间可以使用扫描线,对左端点的对应的数组值加一,右端点减一,然后左到右扫描就可以了
这里要注意几个坑点,代码中已经注释
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>
#define clear(x) memset(x,0,sizeof(x))
using namespace std;
#define LL long long
struct node{
int pos;
double v;
int dir;
};
node dat[100005];
int n;
double bs;
int one[1000005];
int two[1000005];
bool Judge(double t)
{
int sl,sr;
sl = 1e6;
sr = 0;
clear(one);
clear(two);
for(int i = 0;i<n;i++)
{
int pos = dat[i].pos;
int v = dat[i].v;
if(dat[i].dir==1)
{
if(v*t>=pos || (LL)v * (LL)t>=pos)
{
one[0]++;
one[1000000+1]--;
sl = 0;
sr = 1e6;
continue;
}
if((v+bs)<pos/t)
continue;
double t1 = (pos-t * v)/bs;
int r;
if(bs>1000000/t) // 因为有可能这里bs乘t爆范围 double 所以要提前判断 不然挂在42组
r = 1000000;
else
r = bs * t + t1 * v;
r = min(1000000,r);
if(sr<r)sr = r;
if(sl>pos)sl = pos;
one[pos]++;
one[r+1]--;
//cout << "RR:" << pos << r+1 << endl;
}
else
{
pos = 1e6-pos;
if(v*t>=pos || (LL)v * (LL)t>=pos)
{
two[0]++;
two[1000000+1]--;
sl = 0;
sr = 1e6;
continue;
}
if((v+bs)<pos/t)continue;
double t1 = (pos-t*v)/bs;
int l ;
if(t>1e6/bs) // 因为有可能这里bs乘t爆范围 double 所以要提前判断 不然挂在42组
l = 0;
else
l = 1e6-floor(t*bs+t1*v); // 这里要向下取整,因为是反过来的
l = max(0,l);
if(sl>l)sl = l;
if(sr<1e6-pos)sr = 1e6-pos;
two[l]++,two[1000000-pos+1]--;
}
}
int cnt1 = 0;
int cnt2 = 0;
for(int i = sl;i<=sr;i++)
{
cnt1 += one[i];
cnt2 += two[i];
if(cnt1>0 && cnt2>0)return true;
}
return false;
}
double binAns(double s,double e)
{
double mid;
for(int i = 0;i<50;i++)
{
mid = (s+e)/2;
// cout << s << " " << e << " " << mid << endl;
if(Judge(mid))
{
e = mid;
}
else s = mid;
}
return mid;
}
int main()
{
while(scanf("%d%lf",&n,&bs)!=EOF)
{
for(int i = 0;i<n;i++)
{
scanf("%d%lf%d",&dat[i].pos,&dat[i].v,&dat[i].dir);
}
double ans = binAns(0,1e6);
printf("%.10lf\n",ans);
}
return 0;
}