【经典】【模板】扫描线+求平面内相交线段+二分——cf1359F

首先转化为二分答案,在时间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<<'\n';}
    };
    
    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<<'\n';}
    };
    
    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\n",(L+R)/2);
}

 

posted on 2020-05-29 19:07  zsben  阅读(233)  评论(0编辑  收藏  举报

导航