• 2020浙江省赛 H


    https://codeforces.com/gym/102770/problem/H

    题意:

    在二维平面上给定一些点一些线段,定义在x轴上一个点u,如果点u和任意一个给定点v的连线和所有给定线段都不相交(包括端点),则u不在阴影中。问x轴上阴影的长度(>1e9则输出-1)。

    思路:

    首先对每个点预处理出会被线段遮挡的范围。考虑一个点被一条线段遮挡,产生的阴影是一条线段或者一个射线,因为题目要求最后答案>1e9则输出-1,而且给定的点都是整点,因此可以将射线转为一个端点 为INF(1e12)的线段。一个点被遮挡的范围就是它被所有线段遮挡产生的线段的并。求出每个点遮挡范围之后,对所有点的遮挡范围取交集,交集中的长度就是答案。关键就是如何求线段并和线段集合的交。

    解法1:

    求线段并:将线段按左端点排序,从前往后遍历,依次将新的线段和已有的最右边的线段合并即可,保证每次合并后线段之间没有重复区间。

    求线段集合的交:对所有线段集合中的所有线段左右端点进行离散化,建立线段树,对每条线段而言就是在线段树的对应区间上+1,最后询问线段树上哪些区间最大值=n,最小值=n。

    #include <bits/stdc++.h>
    #define rson rt<<1|1
    #define lson rt<<1
    #define mid ((l+r)>>1)
    using namespace std;
    const int maxn=505;
    const double eps = 1e-6;
    const double INF=1e18;
    int sgn(double x) {
        if(fabs(x) < eps)
            return 0;
        if(x < 0)
            return -1;
        return 1;
    }
    struct Point {
        double x,y;
        Point() {}
        Point(double _x,double _y) {
            x = _x;
            y = _y;
        }
        Point operator -(const Point &b)const {
            return Point(x - b.x,y - b.y);
        }
        Point operator +(const Point &b)const {
            return Point(x + b.x,y +b.y);
        }
        double operator ^(const Point &b)const {
            return x*b.y - y*b.x;
        }
        double operator *(const Point &b)const {
            return x*b.x + y*b.y;
        }
    };
    double xmult(Point p0,Point p1,Point p2) { //p0p1 X p0p2
        return (p1-p0)^(p2-p0);
    }
    struct Line{
    	Point s,t;
    	Line(Point X=Point(),Point Y=Point()){
    		s=X,t=Y;
    	}
    };
    Point getIntersectPoint(Line a, Line b) {
        double a1 = a.s.y - a.t.y, b1 = a.t.x - a.s.x, c1 = a.s.x * a.t.y - a.t.x * a.s.y;
        double a2 = b.s.y - b.t.y, b2 = b.t.x - b.s.x, c2 = b.s.x * b.t.y - b.t.x * b.s.y;
        return Point((c1*b2-c2*b1)/(a2*b1-a1*b2), (a2*c1-a1*c2)/(a1*b2-a2*b1));
    }
    Point p[maxn];
    Line l[maxn];
    struct XD{
        double l,r;
    }xd[maxn][maxn];
    int cnt[maxn];
    bool pequal(Point &a,Point &b){
        Point temp=a-b;
        if(sgn(temp.x)==0 && sgn(temp.y)==0)
            return 1;
        else
            return 0;
    }
    XD getShadow(Point a,Line b){
        //特判a在b端点上
        if(pequal(a,b.s)||pequal(a,b.t)){
            return{-INF,INF};
        }
        Point pp[]={b.s,b.t};
        Line x={Point(0,0),Point(10000,0)};
        if(pp[0].y>pp[1].y)swap(pp[0],pp[1]);
        //特判a在b上
        if(pp[0].y<a.y &&  a.y<pp[1].y){
            if(sgn((a-pp[0])^(pp[1]-a))==0){
                return {-INF,INF};
            }
        }
        if(a.y>pp[1].y){
            Line l1={a,pp[0]};
            Line l2={a,pp[1]};
            double xx[2];
            xx[0]=getIntersectPoint(l1,x).x;
            xx[1]=getIntersectPoint(l2,x).x;
            if(xx[0]>xx[1])swap(xx[0],xx[1]);
            return {xx[0],xx[1]};
        }
        else if(a.y>pp[0].y){
            Line l2={a,pp[0]};
            double xx=getIntersectPoint(l2,x).x;
            if(xmult(pp[0],pp[1],a)<0){//直线在点左边
                return {-INF,xx};
            }
            else{
                return {xx,INF};
            }
        }
        else{
            return {0,0};
        }
    }
    bool cmp(XD a,XD b){
        return a.l<b.l;
    }
    double lisan[(maxn*maxn*2)];
    int lisancnt=0;
    
    //区间加,区间最大/最小
    int Tmax[(maxn*maxn*2)<<2],Tmin[(maxn*maxn*2)<<2];
    int lazy[(maxn*maxn*2)<<2];
    void push_up(int rt,int l,int r){
        Tmax[rt]=max(Tmax[lson],Tmax[rson]);
        Tmin[rt]=min(Tmin[lson],Tmin[rson]);
    }
    void push_down(int rt,int l,int r){
        if(!lazy[rt])return;
        Tmax[lson]+=lazy[rt];Tmax[rson]+=lazy[rt];
        Tmin[lson]+=lazy[rt];Tmin[rson]+=lazy[rt];
        lazy[lson]+=lazy[rt];lazy[rson]+=lazy[rt];
        lazy[rt]=0;
    }
    void add(int rt,int l,int r,int ql,int qr,int value){
        if(l>qr || r<ql)return;
        if(ql<=l && r<=qr){
            lazy[rt]+=value;
            Tmax[rt]+=value;
            Tmin[rt]+=value;
            return;
        }
        push_down(rt,l,r);
        if(r==l+1)return;//到叶子节点
        if(ql<=mid-1)add(lson,l,mid,ql,qr,value);
        if(qr>=mid+1)add(rson,mid,r,ql,qr,value);
        push_up(rt,l,r);
    }
    double query(int rt,int l,int r,int value){
        double ans=0;
        if(Tmax[rt]==value && Tmin[rt]==value){
            ans+=lisan[r]-lisan[l];
            return ans;
        }
        push_down(rt,l,r);
        if(Tmax[lson]==value)ans+=query(lson,l,mid,value);
        if(Tmax[rson]==value)ans+=query(rson,mid,r,value);
        return ans;
    }
    void init(int n,int m){
        memset(Tmax,0,sizeof(int)*((n*m*2)*4+2));
        memset(Tmin,0,sizeof(int)*((n*m*2)*4+2));
        memset(lazy,0,sizeof(int)*((n*m*2)*4+2));
        lisancnt=0;
    }
    int main () {
        int T;
        scanf("%d",&T);
        while(T--){
            int n,m;
            scanf("%d%d",&n,&m);
            init(n,m);
            for(int i=1;i<=n;i++){
                scanf("%lf%lf",&p[i].x,&p[i].y);
            }
            for(int i=1;i<=m;i++){
                scanf("%lf%lf%lf%lf",&l[i].s.x,&l[i].s.y,&l[i].t.x,&l[i].t.y);
            }
            for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                    xd[i][j]=getShadow(p[i],l[j]);
                }
                sort(xd[i]+1,xd[i]+1+m,cmp);
                cnt[i]=1;
                for(int j=2;j<=m;j++){
                    if(xd[i][cnt[i]].r>xd[i][j].l){//有重复区域直接合并
                        xd[i][cnt[i]].r=max(xd[i][cnt[i]].r,xd[i][j].r);
                    }
                    else{
                        xd[i][++cnt[i]]=xd[i][j];
                    }
                }
                for(int j=1;j<=cnt[i];j++){
                    lisan[++lisancnt]=xd[i][j].l;
                    lisan[++lisancnt]=xd[i][j].r;
                }
            }
            sort(lisan+1,lisan+1+lisancnt);
            for(int i=1;i<=n;i++){
                for(int j=1;j<=cnt[i];j++){
                    int left=lower_bound(lisan+1,lisan+1+lisancnt,xd[i][j].l)-lisan;
                    int right=lower_bound(lisan+1,lisan+1+lisancnt,xd[i][j].r)-lisan;
                    add(1,1,lisancnt,left,right,1);
                }
            }
            double ans=query(1,1,lisancnt,n);
            if(ans>=1e9){
                puts("-1");
            }
            else
                printf("%.10f
    ",ans);
        }
    }
    

    解法2:

    求线段并:建立map<double,int> m1,对于所有线段在m1上进行差分,即m1[l]++,m1[r]--,最后遍历m1,前缀和>0开始至前缀和=0这样的区间就是最终覆盖范围中的一个线段。

    求线段集合的交:建立map<double,int> m2,对于所有线段集合中所有线段进行差分,即m2[l]++,m2[r]--,之后遍历m2,前缀和=n开始至前缀和<n这样的区间就是一个阴影区间,直接对答案加上贡献即可。

    #include <bits/stdc++.h>
    #define rson rt<<1|1
    #define lson rt<<1
    #define mid ((l+r)>>1)
    using namespace std;
    const int maxn=505;
    const double eps = 1e-6;
    const double INF=1e18;
    int sgn(double x) {
        if(fabs(x) < eps)
            return 0;
        if(x < 0)
            return -1;
        return 1;
    }
    struct Point {
        double x,y;
        Point() {}
        Point(double _x,double _y) {
            x = _x;
            y = _y;
        }
        Point operator -(const Point &b)const {
            return Point(x - b.x,y - b.y);
        }
        Point operator +(const Point &b)const {
            return Point(x + b.x,y +b.y);
        }
        double operator ^(const Point &b)const {
            return x*b.y - y*b.x;
        }
        double operator *(const Point &b)const {
            return x*b.x + y*b.y;
        }
    };
    double xmult(Point p0,Point p1,Point p2) { //p0p1 X p0p2
        return (p1-p0)^(p2-p0);
    }
    struct Line{
    	Point s,t;
    	Line(Point X=Point(),Point Y=Point()){
    		s=X,t=Y;
    	}
    };
    Point getIntersectPoint(Line a, Line b) {
        double a1 = a.s.y - a.t.y, b1 = a.t.x - a.s.x, c1 = a.s.x * a.t.y - a.t.x * a.s.y;
        double a2 = b.s.y - b.t.y, b2 = b.t.x - b.s.x, c2 = b.s.x * b.t.y - b.t.x * b.s.y;
        return Point((c1*b2-c2*b1)/(a2*b1-a1*b2), (a2*c1-a1*c2)/(a1*b2-a2*b1));
    }
    Point p[maxn];
    Line l[maxn];
    struct XD{
        double l,r;
    };
    bool pequal(Point &a,Point &b){
        Point temp=a-b;
        if(sgn(temp.x)==0 && sgn(temp.y)==0)
            return 1;
        else
            return 0;
    }
    XD getShadow(Point a,Line b){
        //特判a在b端点上
        if(pequal(a,b.s)||pequal(a,b.t)){
            return{-INF,INF};
        }
        Point pp[]={b.s,b.t};
        Line x={Point(0,0),Point(10000,0)};
        if(pp[0].y>pp[1].y)swap(pp[0],pp[1]);
        //特判a在b上
        if(pp[0].y<a.y &&  a.y<pp[1].y){
            if(sgn((a-pp[0])^(pp[1]-a))==0){
                return {-INF,INF};
            }
        }
        if(a.y>pp[1].y){
            Line l1={a,pp[0]};
            Line l2={a,pp[1]};
            double xx[2];
            xx[0]=getIntersectPoint(l1,x).x;
            xx[1]=getIntersectPoint(l2,x).x;
            if(xx[0]>xx[1])swap(xx[0],xx[1]);
            return {xx[0],xx[1]};
        }
        else if(a.y>pp[0].y){
            Line l2={a,pp[0]};
            double xx=getIntersectPoint(l2,x).x;
            if(xmult(pp[0],pp[1],a)<0){//直线在点左边
                return {-INF,xx};
            }
            else{
                return {xx,INF};
            }
        }
        else{
            return {0,0};
        }
    }
    bool cmp(XD a,XD b){
        return a.l<b.l;
    }
    int main () {
        int T;
        scanf("%d",&T);
        while(T--){
            int n,m;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++){
                scanf("%lf%lf",&p[i].x,&p[i].y);
            }
            for(int i=1;i<=m;i++){
                scanf("%lf%lf%lf%lf",&l[i].s.x,&l[i].s.y,&l[i].t.x,&l[i].t.y);
            }
            map<double,int>m2;
            for(int i=1;i<=n;i++){
                map<double,int>m1;
                for(int j=1;j<=m;j++){
                    XD temp=getShadow(p[i],l[j]);
                    m1[temp.l]++;
                    m1[temp.r]--;
                }
                int temp=0,flag=0;
                double pre=0;
                for(auto x:m1){
                    temp+=x.second;
                    if(temp>0 && !flag){
                        pre=x.first;
                        flag=1;
                    }
                    else if(temp==0 && flag){
                        m2[pre]++;
                        m2[x.first]--;
                        flag=0;
                    }
                }
            }
            int temp=0,flag=0;
            double pre=0,ans=0;
            for(auto x:m2){
                temp+=x.second;
                if(temp==n && !flag){
                    pre=x.first;
                    flag=1;
                }
                else if(temp<n && flag){
                    ans+=x.first-pre;
                    flag=0;
                }
            }
            if(ans>1e9)
                puts("-1");
            else
                printf("%.10f
    ",ans);
        }
    }
    
  • 相关阅读:
    JavaScript 闭包(转)
    JavaScript 获取键盘扫描码
    前台网站优化方案
    设计模式之装饰者模式
    设计模式之蝇量模式
    设计模式之策略模式
    Algorithm学习之any_of
    Algorithm学习之all_of学习
    Algorithm学习之adjacent_find学习
    数据结构-表达式求值
  • 原文地址:https://www.cnblogs.com/ucprer/p/13850858.html
Copyright © 2020-2023  润新知