首先转化为二分答案,在时间t内有两车相撞,等价于有两辆车在t时间内运动的轨迹相交
所以问题变成判断一个平面内是否存在两条相交线段,经典问题
bool operator<(const line &a,const line &b){//set内部的排列方式是左端点+斜率 db x=max(min(a.p[0].x,a.p[1].x),min(b.p[0].x,b.p[1].x)); return sign(a.get_y(x)-b.get_y(x))<=0; } struct has_inter{ //判平面上是否有线段相交 /* 将线段端点按x轴排序,从左到右扫描,用一个set维护当前被扫描到的线段,set内部的排列方式是左端点+斜率 扫到左端点: 找到该端点代表线段在set里的前驱prv后继nxt,分别判断是否和该线段相交 将该线段加入set 扫到右端点: 找到该端点代表线段在set里的前驱prv后继nxt,判断prv和nxt是否相交(想想为什么要判这么一下) 将该线段从set移除 */ struct event{ db x;int tp,id; event(){} event(db x,int tp,int id):x(x),tp(tp),id(id){} bool operator<(const event& e)const {//按x坐标排序 if(sign(x-e.x)==0)return tp>e.tp; return sign(x-e.x)<0; } void print(){cout<<x<<" "<<tp<<" "<<id<<' ';} }; set<line>s; //维护当前扫描到的线段 vector<set<line>::iterator >where;// set内线段的迭代器 inline set<line>::iterator prev(set<line>::iterator it){ return it == s.begin()?s.end():--it; } inline set<line>::iterator next(set<line>::iterator it){ return ++it; } pair<int,int> solve(vector<line> &a){ int n=a.size(); vector<event> e; for(int i=0;i<n;i++){ e.push_back(event(min(a[i].p[0].x,a[i].p[1].x),+1,i)); e.push_back(event(max(a[i].p[0].x,a[i].p[1].x),-1,i)); } sort(e.begin(),e.end());// 给所有端点排序 s.clear(); where.resize(a.size()); for(int i=0;i<e.size();i++){ int id=e[i].id; if(e[i].tp==+1){ // 是左端点 auto nxt=s.lower_bound(a[id]);//找前驱后继 auto prv=prev(nxt); if(nxt!=s.end() && intersect(*nxt,a[id])) return make_pair(nxt->id,id); if(prv!=s.end() && intersect(*prv,a[id])) return make_pair(prv->id,id); where[id] = s.insert(nxt,a[id]); }else { // 是右端点 if (where[id] == s.end()) continue; auto nxt = next(where[id]), prv = prev(where[id]); if (nxt != s.end() && prv != s.end() && intersect(*nxt, *prv)) return make_pair(prv->id, nxt->id); s.erase(where[id]); } } return make_pair(-1,-1); } };
ac代码
#include<bits/stdc++.h> using namespace std; #define N 300005 typedef double db; const db eps=1e-10; const db pi=acos(-1); int sign(db k){ if (k>eps) return 1; else if (k<-eps) return -1; return 0; } int cmp(db k1,db k2){return sign(k1-k2);} int inmid(db k1,db k2,db k3){return sign(k1-k3)*sign(k2-k3)<=0;}// k3 在 [k1,k2] 内 struct point{ db x,y;int id; point operator + (const point &k1) const{return (point){k1.x+x,k1.y+y};} point operator - (const point &k1) const{return (point){x-k1.x,y-k1.y};} point operator * (db k1) const{return (point){x*k1,y*k1};} point operator / (db k1) const{return (point){x/k1,y/k1};} int operator == (const point &k1) const{return cmp(x,k1.x)==0&&cmp(y,k1.y)==0;} }; int inmid(point k1,point k2,point k3){return inmid(k1.x,k2.x,k3.x)&&inmid(k1.y,k2.y,k3.y);} db cross(point k1,point k2){return k1.x*k2.y-k1.y*k2.x;} db dot(point k1,point k2){return k1.x*k2.x+k1.y*k2.y;} db rad(point k1,point k2){return atan2(cross(k1,k2),dot(k1,k2));} struct line{ point p[2];int id; line(point k1,point k2,int i){p[0]=k1; p[1]=k2; id=i;} point& operator [] (int k){return p[k];} db get_y(db x)const{ if(sign(p[0].x-p[1].x)==0)return p[0].y; return p[0].y+(p[1].y-p[0].y)*(x-p[0].x)/(p[1].x-p[0].x); } }; int intersect(db l1,db r1,db l2,db r2){ if (l1>r1) swap(l1,r1); if (l2>r2) swap(l2,r2); return cmp(r1,l2)!=-1&&cmp(r2,l1)!=-1; } int checkSS(point k1,point k2,point k3,point k4){ return intersect(k1.x,k2.x,k3.x,k4.x)&&intersect(k1.y,k2.y,k3.y,k4.y)&& sign(cross(k3-k1,k4-k1))*sign(cross(k3-k2,k4-k2))<=0&& sign(cross(k1-k3,k2-k3))*sign(cross(k1-k4,k2-k4))<=0; } int intersect(line a,line b){ return checkSS(a[0],a[1],b[0],b[1]); } int n; db x[N],y[N],dx[N],dy[N],s[N]; point k1,k2; bool operator<(const line &a,const line &b){//set内部的排列方式是左端点+斜率 db x=max(min(a.p[0].x,a.p[1].x),min(b.p[0].x,b.p[1].x)); return sign(a.get_y(x)-b.get_y(x))<=0; } struct has_inter{ //判平面上是否有线段相交 /* 将线段端点按x轴排序,从左到右扫描,用一个set维护当前被扫描到的线段,set内部的排列方式是左端点+斜率 扫到左端点: 找到该端点代表线段在set里的前驱prv后继nxt,分别判断是否和该线段相交 将该线段加入set 扫到右端点: 找到该端点代表线段在set里的前驱prv后继nxt,判断prv和nxt是否相交(想想为什么要判这么一下) 将该线段从set移除 */ struct event{ db x;int tp,id; event(){} event(db x,int tp,int id):x(x),tp(tp),id(id){} bool operator<(const event& e)const {//按x坐标排序 if(sign(x-e.x)==0)return tp>e.tp; return sign(x-e.x)<0; } void print(){cout<<x<<" "<<tp<<" "<<id<<' ';} }; set<line>s; //维护当前扫描到的线段 vector<set<line>::iterator >where;// set内线段的迭代器 inline set<line>::iterator prev(set<line>::iterator it){ return it == s.begin()?s.end():--it; } inline set<line>::iterator next(set<line>::iterator it){ return ++it; } pair<int,int> solve(vector<line> &a){ int n=a.size(); vector<event> e; for(int i=0;i<n;i++){ e.push_back(event(min(a[i].p[0].x,a[i].p[1].x),+1,i)); e.push_back(event(max(a[i].p[0].x,a[i].p[1].x),-1,i)); } sort(e.begin(),e.end());// 给所有端点排序 s.clear(); where.resize(a.size()); for(int i=0;i<e.size();i++){ int id=e[i].id; if(e[i].tp==+1){ // 是左端点 auto nxt=s.lower_bound(a[id]);//找前驱后继 auto prv=prev(nxt); if(nxt!=s.end() && intersect(*nxt,a[id])) return make_pair(nxt->id,id); if(prv!=s.end() && intersect(*prv,a[id])) return make_pair(prv->id,id); where[id] = s.insert(nxt,a[id]); }else { // 是右端点 if (where[id] == s.end()) continue; auto nxt = next(where[id]), prv = prev(where[id]); if (nxt != s.end() && prv != s.end() && intersect(*nxt, *prv)) return make_pair(prv->id, nxt->id); s.erase(where[id]); } } return make_pair(-1,-1); } }; int judge(db t){//经过时间t vector<line>segs; for(int i=0;i<n;i++){ db d=sqrt(dx[i]*dx[i]+dy[i]*dy[i]); k1=(point){ x[i],y[i],i}; k2=(point){ x[i]+t*s[i]*dx[i]/d,y[i]+t*s[i]*dy[i]/d,i }; line l=line(k1,k2,i); segs.push_back(l); } has_inter Q; return Q.solve(segs).first!=-1; } int main(){ cin>>n; for(int i=0;i<n;i++) cin>>x[i]>>y[i]>>dx[i]>>dy[i]>>s[i]; int T=100; db L=0,R=1e10,mid,flag=0; while(T--){ mid=(L+R)/2; if(judge(mid)) R=mid,flag=1; else L=mid; } if(!flag)cout<<"No show :("; else printf("%.6lf ",(L+R)/2); }