prow2012-A3-0011

从 Trac 迁移的文章

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

原文章内容如下:

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

出这题的初衷是考察直线点向式的应用,一条直线可以用(x0,y0) + k*(X,Y)表示,其中k就是向量增量的倍数。

k一开始是一个[0,1]的区间,随着限制矩形的增加,区间可以覆盖、延长、擦除,

现在的问题就是,得到每个矩形的区间后,如何区间并和区间擦除。

我用的是扫描线的方法,对于每一段,询问当前最大标号的区间是染色还是擦除,以决定这段是否能计入答案。

代码比较清晰,推荐不会做这题的同学们阅读这篇代码学习。



{{{


#include <vector>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <set>
#include <map>
#include <sstream>
#include <string>
#include <math.h>
using namespace std;
map<double,int> mat;
double calcK(double x0,double X,double x1){
    if (X==0) return 0;
    return (x1-x0)/X;
}
double Unoin(double &L,double &R,double l,double r){
    if (l>r) swap(l,r);   
    L= max(L,l);
    R = min(R,r);
}
double Num[4010],x1[110],Y1[110],x2[110],y2[110],
       X[2010],Y[2010],L[2010],R[2010];
int N,M;
bool mark[4010];
int style[1010];
typedef pair<double,int> PII;
set<PII> tim;
set<int> MX;
double
calc(double x0, double y0, double X, double Y)
{
    X -= x0, Y -= y0;
    int i,j,t=0;
    tim.clear();
    for (i = 0; i < N; i++) {
        L[i] = 0, R[i] = 1;
        if (X==0){
            if (x0>=x1[i]&&x0<=x2[i]){
                Unoin(L[i],R[i],calcK(y0,Y,Y1[i]),calcK(y0,Y,y2[i]));
            } else L[i] = R[i] = 0;
        }else if (Y==0){
            if (y0>=Y1[i]&&y0<=y2[i]){
                Unoin(L[i],R[i],calcK(x0,X,x1[i]),calcK(x0,X,x2[i]));
            } else L[i] = R[i] = 0;
        }else 
        {       Unoin(L[i],R[i],calcK(y0,Y,Y1[i]),calcK(y0,Y,y2[i]));
            Unoin(L[i],R[i],calcK(x0,X,x1[i]),calcK(x0,X,x2[i]));
        }
        if (L[i]>=R[i]) continue;
        tim.insert(PII(L[i],~i));
        tim.insert(PII(R[i],i));
    } 
    MX.clear();
    double last = 0,ans = 0;
    for (set<PII>::iterator p = tim.begin();p!=tim.end();p++){

        if (!MX.empty()&&style[*MX.rbegin()]==1) ans+=p->first-last;
        if (p->second<0) MX.insert(~p->second);
        else MX.erase(p->second);
        last = p->first;
    }


    return sqrt((ans*ans)*(X*X+Y*Y));

}
int
main()
{
    int i;
    //freopen("input","r",stdin);
    //freopen("output","w",stdout);
    while (scanf("%d%d", &N, &M) != EOF) {
        for (i = 0; i < N ; i++)
            scanf("%d%lf%lf%lf%lf",&style[i], &x1[i], &Y1[i], &x2[i], &y2[i]);
        double ans = 0;
        for (i = 0; i < M; i++) {
            scanf("%lf%lf", &X[i],&Y[i]);
            if (i>0)
                ans+=calc(X[i-1],Y[i-1],X[i],Y[i]);

        }
        printf("%.2lf\n",ans);
    }
}


}}}

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

出这题的初衷是考察直线点向式的应用,一条直线可以用(x0,y0) + k*(X,Y)表示,其中k就是向量增量的倍数。

k一开始是一个[0,1]的区间,随着限制矩形的增加,区间可以覆盖、延长、擦除,

现在的问题就是,得到每个矩形的区间后,如何区间并和区间擦除。

我用的是扫描线的方法,对于每一段,询问当前最大标号的区间是染色还是擦除,以决定这段是否能计入答案。

代码比较清晰,推荐不会做这题的同学们阅读这篇代码学习。

#include <vector>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <set>
#include <map>
#include <sstream>
#include <string>
#include <math.h>
using namespace std;
map<double,int> mat;
double calcK(double x0,double X,double x1){
    if (X==0) return 0;
    return (x1-x0)/X;
}
double Unoin(double &L,double &R,double l,double r){
    if (l>r) swap(l,r);   
    L= max(L,l);
    R = min(R,r);
}
double Num[4010],x1[110],Y1[110],x2[110],y2[110],
       X[2010],Y[2010],L[2010],R[2010];
int N,M;
bool mark[4010];
int style[1010];
typedef pair<double,int> PII;
set<PII> tim;
set<int> MX;
double
calc(double x0, double y0, double X, double Y)
{
    X -= x0, Y -= y0;
    int i,j,t=0;
    tim.clear();
    for (i = 0; i < N; i++) {
        L[i] = 0, R[i] = 1;
        if (X==0){
            if (x0>=x1[i]&&x0<=x2[i]){
                Unoin(L[i],R[i],calcK(y0,Y,Y1[i]),calcK(y0,Y,y2[i]));
            } else L[i] = R[i] = 0;
        }else if (Y==0){
            if (y0>=Y1[i]&&y0<=y2[i]){
                Unoin(L[i],R[i],calcK(x0,X,x1[i]),calcK(x0,X,x2[i]));
            } else L[i] = R[i] = 0;
        }else 
        {       Unoin(L[i],R[i],calcK(y0,Y,Y1[i]),calcK(y0,Y,y2[i]));
            Unoin(L[i],R[i],calcK(x0,X,x1[i]),calcK(x0,X,x2[i]));
        }
        if (L[i]>=R[i]) continue;
        tim.insert(PII(L[i],~i));
        tim.insert(PII(R[i],i));
    } 
    MX.clear();
    double last = 0,ans = 0;
    for (set<PII>::iterator p = tim.begin();p!=tim.end();p++){
        if (!MX.empty()&&style[*MX.rbegin()]==1) ans+=p->first-last;
        if (p->second<0) MX.insert(~p->second);
        else MX.erase(p->second);
        last = p->first;
    }
    return sqrt((ans*ans)*(X*X+Y*Y));
}
int
main()
{
    int i;
    //freopen("input","r",stdin);
    //freopen("output","w",stdout);
    while (scanf("%d%d", &N, &M) != EOF) {
        for (i = 0; i < N ; i++)
            scanf("%d%lf%lf%lf%lf",&style[i], &x1[i], &Y1[i], &x2[i], &y2[i]);
        double ans = 0;
        for (i = 0; i < M; i++) {
            scanf("%lf%lf", &X[i],&Y[i]);
            if (i>0)
                ans+=calc(X[i-1],Y[i-1],X[i],Y[i]);
        }
        printf("%.2lf\n",ans);
    }
}