2012-A3-0011

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

题意是求,在平面上每次对若干个矩形区域依次进行染色和插除操作,然后给出一条折线段,求这个折线段在染色区域内的长度。

显然只要求出折线段上每一段线段的染色部分长度即可。

对于每条线段关于每个矩形求一个相交的区间。

这个区间可以表示成 [l,r],0<=l<=r<=1,表示占用区间的比例,然后就可以对区间依次进行染色和消色操作了。

这个可以用O(n^2^),也可以O(n log n),此题的用n^2^的也可以过.

麻烦的地方在于求出一条线段和一个矩形的公共相交部分。

对于平行坐标轴的矩形,可以用一种比较简单的方法求出,首先把矩形看成2组平行线段,每组线段中间部分对线段求交,这个只要把相应的x或y坐标带入即可求得,然后归一化至[l,r],0<=l<=r<=1

然后再对另一组求一个[l',r'],最后就再对[0,1],[l,r],[l',r']求交,即可求出线段和矩形的交。之后的操作就都比较方便了,不详述了。


{{{
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
typedef double real;
typedef pair<real,real> itv;
const real eps = 1e-8;
inline real dv(int r0,int dr,int r1){
    return (real)(r1-r0) / dr;
}
inline void andi(real & a ,real & b ,real x ,real y){
    if(x>y)swap(x,y);
    if(a<x)a=x;
    if(b>y)b=y;
}
vector<itv> ha;
inline void cmax(real&a,real b){if(a<b)a=b;}
inline void cmin(real&a,real b){if(a>b)a=b;}
int xl[1024],yl[1024],xr[1024],yr[1024],t[1024];
int main(){
    int x,y,x0,y0;
    for(int n,m;scanf("%d%d",&n,&m)==2;){
        for(int i=0;i<n;++i)
            scanf("%d%d%d%d%d",t+i,xl+i,yl+i,xr+i,yr+i);
        scanf("%d%d",&x0,&y0);
        real ans =0,l,r;
        for(int i=1;i<m;(++i),(x0+=x),(y0+=y)){
            scanf("%d%d",&x,&y);
            x-=x0;y-=y0;
            if(!(x||y))continue;
            ha.clear();
            for(int j=0;j<n;++j){
                l =0;r=1;
                if(x&&y){
                    andi(l,r,dv(y0,y,yl[j]),dv(y0,y,yr[j]));
                    andi(l,r,dv(x0,x,xl[j]),dv(x0,x,xr[j]));
                    if(l>r)l=r=-1;
                }else
                if(x==0){
                    if(x0>=xl[j] && x0<=xr[j])
                        andi(l,r,dv(y0,y,yl[j]),dv(y0,y,yr[j]));
                    else l = r =-1;
                }else
                if(y==0){
                    if(y0>=yl[j]&& y0<=yr[j])
                        andi(l,r,dv(x0,x,xl[j]),dv(x0,x,xr[j]));
                    else l = r =-1;
                }
                if(r-l<eps)continue;
                if(t[j]==1){
                    ha.push_back(make_pair(l,r));
                    sort(ha.begin(),ha.end());
                    size_t i,j;
                    for(i=0,j=1;i<ha.size();){
                        for(;j<ha.size() &&
                             ha[j].first <= ha[i].second;++j){
                                cmax(ha[i].second ,ha[j].second);
                        }
                        ++i;
                        if(j<ha.size()){
                            ha[i]=ha[j];
                        }else break;
                    }
                    ha.resize(i);
                }else{
                    for(size_t i=0;i<ha.size();++i){
                        if(ha[i].first<l && ha[i].second>r){
                            ha.push_back(make_pair(r,ha[i].second));
                            ha[i].second =l;
                            break;
                        }
                        if(ha[i].first>=l && ha[i].second <=r)
                            ha[i].first = 99;
                        if(ha[i].first<l && ha[i].second>l)
                            ha[i].second=l;
                        if(ha[i].first<r && ha[i].second>r)
                            ha[i].first=r;
                    }
                    sort(ha.begin(),ha.end());
                    while(ha.size()&&ha.rbegin()->first>1)ha.pop_back();
                }
            }
            real len =0;
            for(size_t i=0;i<ha.size();++i)
                len+= ha[i].second-ha[i].first;
            //printf("%lf\n",len*hypot(x,y));
            ans += len*hypot(x,y);
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}
}}}

题意是求,在平面上每次对若干个矩形区域依次进行染色和插除操作,然后给出一条折线段,求这个折线段在染色区域内的长度。

显然只要求出折线段上每一段线段的染色部分长度即可。

对于每条线段关于每个矩形求一个相交的区间。

这个区间可以表示成 [l,r],0<=l<=r<=1,表示占用区间的比例,然后就可以对区间依次进行染色和消色操作了。

这个可以用O(n2),也可以O(n log n),此题的用n2的也可以过.

麻烦的地方在于求出一条线段和一个矩形的公共相交部分。

对于平行坐标轴的矩形,可以用一种比较简单的方法求出,首先把矩形看成2组平行线段,每组线段中间部分对线段求交,这个只要把相应的x或y坐标带入即可求得,然后归一化至[l,r],0<=l<=r<=1

然后再对另一组求一个[l',r'],最后就再对[0,1],[l,r],[l',r']求交,即可求出线段和矩形的交。之后的操作就都比较方便了,不详述了。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
typedef double real;
typedef pair<real,real> itv;
const real eps = 1e-8;
inline real dv(int r0,int dr,int r1){
    return (real)(r1-r0) / dr;
}
inline void andi(real & a ,real & b ,real x ,real y){
    if(x>y)swap(x,y);
    if(a<x)a=x;
    if(b>y)b=y;
}
vector<itv> ha;
inline void cmax(real&a,real b){if(a<b)a=b;}
inline void cmin(real&a,real b){if(a>b)a=b;}
int xl[1024],yl[1024],xr[1024],yr[1024],t[1024];
int main(){
    int x,y,x0,y0;
    for(int n,m;scanf("%d%d",&n,&m)==2;){
        for(int i=0;i<n;++i)
            scanf("%d%d%d%d%d",t+i,xl+i,yl+i,xr+i,yr+i);
        scanf("%d%d",&x0,&y0);
        real ans =0,l,r;
        for(int i=1;i<m;(++i),(x0+=x),(y0+=y)){
            scanf("%d%d",&x,&y);
            x-=x0;y-=y0;
            if(!(x||y))continue;
            ha.clear();
            for(int j=0;j<n;++j){
                l =0;r=1;
                if(x&&y){
                    andi(l,r,dv(y0,y,yl[j]),dv(y0,y,yr[j]));
                    andi(l,r,dv(x0,x,xl[j]),dv(x0,x,xr[j]));
                    if(l>r)l=r=-1;
                }else
                if(x==0){
                    if(x0>=xl[j] && x0<=xr[j])
                        andi(l,r,dv(y0,y,yl[j]),dv(y0,y,yr[j]));
                    else l = r =-1;
                }else
                if(y==0){
                    if(y0>=yl[j]&& y0<=yr[j])
                        andi(l,r,dv(x0,x,xl[j]),dv(x0,x,xr[j]));
                    else l = r =-1;
                }
                if(r-l<eps)continue;
                if(t[j]==1){
                    ha.push_back(make_pair(l,r));
                    sort(ha.begin(),ha.end());
                    size_t i,j;
                    for(i=0,j=1;i<ha.size();){
                        for(;j<ha.size() &&
                             ha[j].first <= ha[i].second;++j){
                                cmax(ha[i].second ,ha[j].second);
                        }
                        ++i;
                        if(j<ha.size()){
                            ha[i]=ha[j];
                        }else break;
                    }
                    ha.resize(i);
                }else{
                    for(size_t i=0;i<ha.size();++i){
                        if(ha[i].first<l && ha[i].second>r){
                            ha.push_back(make_pair(r,ha[i].second));
                            ha[i].second =l;
                            break;
                        }
                        if(ha[i].first>=l && ha[i].second <=r)
                            ha[i].first = 99;
                        if(ha[i].first<l && ha[i].second>l)
                            ha[i].second=l;
                        if(ha[i].first<r && ha[i].second>r)
                            ha[i].first=r;
                    }
                    sort(ha.begin(),ha.end());
                    while(ha.size()&&ha.rbegin()->first>1)ha.pop_back();
                }
            }
            real len =0;
            for(size_t i=0;i<ha.size();++i)
                len+= ha[i].second-ha[i].first;
            //printf("%lf\n",len*hypot(x,y));
            ans += len*hypot(x,y);
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}