2020浙江省赛 H - Huge Clouds(线段树/差分求线段交并)

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\n",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\n",ans);
    }
}
posted @ 2020-10-21 10:15  UCPRER  阅读(369)  评论(0编辑  收藏  举报