zoukankan      html  css  js  c++  java
  • 【经典】【模板】扫描线+求平面内相交线段+二分——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<<'
    ';}
        };
        
        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);
    }
  • 相关阅读:
    使用docker sail镜像创建laravel项目
    Python使用阿里云镜像
    VMware安装OpenEuler虚拟机并配置图形界面
    机器学习模型评估指标汇总 (一)
    机器学习模型评估指标汇总 (二)
    运行docker命令需要sudo权限的问题
    如何不重装修复损坏的 Ubuntu 系统
    卡尔曼滤波
    升级anaconda
    jupyter notebook 代码自动补全
  • 原文地址:https://www.cnblogs.com/zsben991126/p/12989199.html
Copyright © 2011-2022 走看看