HDU 4629 Burning

/*

  题意: 求平面的n个三角形的不同覆盖次数(1 - n)的面积 。

  分析:扫描线可以搞定。

         扫描线:把三角形的顶点和交点的x轴的值排序, 这样就能把三角形给离散化, 然后枚举前后两条线之间的情况来计算。
          前后两条搞毛线之间的情况变得很简单:都是由没有线交属于某个三角形的三角形或者梯形组成, 然后就可以从上到下遍历这些图形边, 依次计算面积。

具体如图:

                         

*/


#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <queue>
#include <deque>
#include <complex>

#define MAX 200005
#define INF 1e8
#define FOR(i, n) for(int i  = 0; i < n; i++)
#define FORB(i, n) for(int i = n - 1; i >= 0; i--)
#define MP(i, j) make_pair(i, j)
#define MEM(array, val) memset(array, val, sizeof(array))
#define pu first
#define pv second
#define eps 1e-10
#define INF 1e10
#define next(i, n) ( (i + 1) %n)
using namespace std;
inline double sqr(double x) {
    return x * x;
}
inline int sgn(double x) {
    return x > eps ? 1 : (x < -eps? -1 : 0);
}
struct Point {
    double x, y;
    Point() {}
    Point(const double &xx, const double &yy):x(xx), y(yy) {}
    friend Point operator + (const Point &b, const Point &a) {
        return Point(b.x + a.x, b.y + a.y);
    }
    friend Point operator - (const Point &b, const Point &a) {
        return Point(b.x - a.x, b.y - a.y);
    }
    Point operator * (const double &a)const{
        return Point(x * a, y * a);
    }
    Point operator / (const double &a) const{
        return Point(x / a, y / a);
    }
    friend double dot(const Point &a, const Point &b) {
        return a.x *b.x + a.y * b.y;
    }
    friend double det(const Point &a, const Point &b) {
        return a.x * b.y - a.y * b.x;
    }
    double len( )const {
        return sqrt(dot(*this, *this));
    }
    bool operator < (const Point &a)const {
        return sgn(x - a.x) < 0 || (sgn(x - a.x) == 0 && sgn(y - a.y) > 0);
    }
    bool operator == (const Point &a)const {
        return sgn(x - a.x ) == 0 && sgn(y - a.y) == 0;
    }
    void in() {
        scanf("%lf %lf", &x, &y);
    }
    void out()const {
        printf("%.4f    %.4f\n", x, y);
    }
};
struct Line{
    Point s, t;
    Line(){}
    Line(const Point &ss, const Point &tt):s(ss), t(tt){}
    friend bool isParallel(const Line &l1, const Line &l2){
        return sgn(det(l1.t - l1.s, l2.t - l2.s) ) == 0;
    }
    //点是否在线上(包含边界)
    friend bool pointOnLine(const Point &p, const Line &l1){
        bool res1 = sgn(det(l1.s - p, l1.t - p)) == 0;
        if(!res1)return false;
        return sgn( dot(l1.s - p, l1.t - p) ) <= 0;
    }
    //ab是否在l1的同一边, 不包含边界
    friend bool sameSide(const Line &l1, const Point &a, const Point &b){
        double res1 = det(b - l1.s, l1.t - l1.s);
        double res2 = det(a - l1.s, l1.t - l1.s);
//        cout<<sgn( res1 * res2)<<endl;
        return sgn( res1 * res2) > 0;
    }
    //线段和线段的交点(包含边界), 重合则返回相交的端点
    friend vector<Point> lineInsectLine(const Line &l1, const Line &l2){
        vector<Point>res;
        if(isParallel(l1, l2)){
            if(pointOnLine(l1.s, l2))res.push_back(l1.s);
            if(pointOnLine(l1.t, l2))res.push_back(l1.t);
            if(pointOnLine(l2.s, l1))res.push_back(l2.s);
            if(pointOnLine(l2.t, l1))res.push_back(l2.t);
        }else{
            bool res1 = !sameSide(l1, l2.s, l2.t);
            bool res2 = !sameSide(l2, l1.s, l1.t);
            if(!res1 || !res2)return res;
            double s1 = det(l1.s - l2.s, l2.t - l2.s), s2 = det(l1.t - l2.s, l2.t - l2.s);
            res.push_back(( l1.t * s1 - l1.s * s2) / (s1 - s2));
        }
        return res;
    }
    void out()const{
        s.out();
        t.out();
    }
};
//ba 到 bc 的叉积符号
int cross(const Point &a,const Point &b, const Point &c) {
    return sgn(det(a - b , c - b));
}

int n;//三角形个数
int nx, nl;//扫描线个数,  区间线个数
double x[111222];//扫描线
double ans[111];//每层的面积
struct Traingle {
    Point a[3];
    Traingle() {}
    Traingle(const Point &aa, const Point &bb, const Point &cc) {
        a[0] = aa; a[1] = bb; a[2] = cc;
    }
    //返回两个三角形的所有交点
    friend vector<Point> insectTrian(const Traingle &a, const Traingle &b){
        vector<Point>res, tt;
        for(int i = 0; i < 3; i++){
            Line l1(a.a[i], a.a[next(i, 3)]);
            for(int j  = 0; j < 3; j++){
                    Line l2(b.a[j], b.a[next(j,3)]);
                    tt = lineInsectLine(l1, l2);
                    for(int k = 0; k < tt.size();k++){
                        res.push_back(tt[k]);
                    }
            }
        }
        //去重
        sort(res.begin(), res.end());
        int tn = unique(res.begin(), res.end()) - res.begin();
        res.resize(tn);
        return res;
    }
    //返回三角形和线的交点
    friend vector<Point> insectLine(const Traingle &b, const Line &a){
        vector<Point>res, tt;
        for(int i = 0; i < 3; i++){
            Line line(b.a[i], b.a[next(i, 3)]);
            tt = lineInsectLine(line, a);
            for(int i = 0; i < tt.size(); i++){
                res.push_back(tt[i]);
            }
        }
        //去重
        sort(res.begin(), res.end());//左到右, 上到下
        int tn= unique(res.begin(), res.end()) - res.begin();
        res.resize(tn);
        return res;
    }
    //判断面积是否为0, 看有木有共线边
    bool isZroArea(){
        return cross( a[0], a[1], a[2]) == 0;
    }
} train[55];
//区间斜线类, 辅助计算区间面积
struct rangLine{
    double yl, yr;//斜线在区间左右边的y值
    int type; //1 表示某三角形在该区间的上边, -1下边
    bool operator < (const rangLine &a)const{
        return (yl + yr) > (a.yl + a.yr);
    }
}rl[111222];

void addx(double val){
    x[nx++] = val;
}
void init(){
    cin>>n;
    int p = 0;
    for(int i = 0; i < n; i++) {
            train[p].a[0].in();
            train[p].a[1].in();
            train[p++].a[2].in();
            if(train[p -1 ].isZroArea() )p--;//面积为0滚蛋
    }
    n = p;
    nx = 0;
    nl = 0;
    memset(ans, 0, sizeof(ans));
}
//获取所有扫描线, 并且去重
void getScanLine(){
    vector<Point>ps;
    for(int i = 0; i < n; i++){
        addx(train[i].a[0].x);
        addx(train[i].a[1].x);
        addx(train[i].a[2].x);
        for(int j =i + 1; j < n; j++){
            ps = insectTrian(train[i], train[j]);
            for(int k = 0; k < ps.size(); k++){
                addx(ps[k].x);
            }
        }
    }
    sort(x, x + nx);
    //去重
    int p = 0;
    for(int i = 0; i < nx; i++){
        if(!i || sgn(fabs(x[i] - x[i -1]) != 0) ){
            x[p++] = x[i];
        }
    }
    nx = p;
}
const double maxh = 5000;
//获取某区间单个三角形的区间线
void getRangLine(const Traingle &tri, const double &l, const double &r){
//    cout<<"at range "<<l<<" "<<r<<endl;
    //判断三角形是否在区间内
    double minx  = INF, maxx = -INF;
    for(int i = 0; i < 3; i++){
        minx = min(tri.a[i].x, minx);
        maxx = max(tri.a[i].x, maxx);
    }
    if( sgn(l - minx) < 0 || sgn(r - maxx) > 0)return ;//线必须夹着三角形
    //求交点
    vector<Point>psl, psr;
    Line ll(Point(l, -maxh), Point(l, maxh));
    Line lr(Point(r, -maxh), Point(r, maxh));
    //获取两边直线交点
    psl = insectLine(tri, ll);
    psr = insectLine(tri, lr);
    int i  = 0, j = 0;
    rl[nl].yl = psl[i].y;
    rl[nl].yr = psr[j].y;
    rl[nl].type = 1;
    if(i < psl.size() - 1)i++;
    if(j < psr.size() - 1)j++;
    rl[nl + 1].yl = psl[i].y;
    rl[nl + 1].yr = psr[j].y;
    rl[nl + 1].type = -1;
    nl += 2;
}
//单个区间的面积计算情况
void cacuRang(const double &l, const double &r){
        nl  = 0;
        for(int j = 0; j < n; j++){
            getRangLine(train[j], l, r);
        }
        sort(rl, rl + nl);
        int cnt = 0;
        for(int i  = 0; i < nl; i++){
            if(cnt){
                ans[cnt] += fabs((rl[i  - 1].yl - rl[i].yl + rl[i - 1].yr - rl[i].yr) * (r- l) );
            }
            cnt += rl[i].type;
        }
}
void solve( ){
    for(int i =1; i < nx; i++){
        cacuRang(x[i -1], x[i]);
    }
    for(int i = 1; i <= n; i++){
        printf("%.10f\n", ans[i]/2.0);
    }
}
int main() {
    int T;
    cin>>T;
    while(T--) {
        init();
        getScanLine();
        solve();
    }
    return 0;
}
/*
2
2
0 0
0 2
2 0
0 1
0 -1
2 -1
1 2
1 0
3 0
*/




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值