URAL 1464 Light

在一个简单多边形内有一个灯泡,问灯泡能够照到的面积。我是看了封尘记忆的blog的,大概理解了。


主要看这个图,然后和代码结合起来看。。多边形是ABCDE,因为EA边跨过了x轴负半轴,所以多添加一个在负半轴上的点M。扫描线扫过去阴影部分面积答案加起来就好了。

扫描线有一条入点和一个出点,point里面有重载小于号,是保证入点的极角小于出点的极角。seg重载小于号,新插入的线段必须得跟已经插入的线段,按照到原点的距离排序。然后就是用multiset。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>

using namespace std;

#define LL long long
#define eps 1e-8
#define inf 0x3f3f3f3f
#define Pi acos( -1.0 )
#define pb push_back
#define mnx 60100
#define mxn 200

int dcmp( double x ){
    if( fabs( x ) < eps ) return 0;
    return x < 0 ? -1 : 1;
}
struct point{
    double x, y, ang;
    point( double x = 0, double y = 0 ) : x(x), y(y) {
        ang = atan2( y, x );
    }
    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 );
    }
    point operator * ( const double &k ) const{
        return point( x * k, y * k );
    }
    point operator / ( const double &k ) const{
        return point( x / k, y / k );
    }
    bool operator < ( const point &b ) const{
        return ang > b.ang;
    }
    bool operator == ( const point &b ) const{
        return dcmp( x - b.x ) == 0 && dcmp( y - b.y ) == 0;
    }
    double len(){
        return sqrt( x * x + y * y );
    }
};
typedef point Vector;
double dot( Vector a, Vector b ){
    return a.x * b.x + a.y * b.y;
}
double cross( Vector a, Vector b ){
    return a.x * b.y - a.y * b.x;
}
point get_intersection( point au, point av, point bu, point bv ){
    Vector u = au - bu, aV = av - au, bV = bv - bu;
    double t = cross( bV, u ) / cross( aV, bV );
    return au + aV * t;
}
const point o = point( 0, 0 );
point base;
struct Seg{
    point u, v;
    int id, in;
    double ang;
    Seg(){}
    Seg( point u, point v, int in, int id, double ang ) : u(u), v(v), in(in), id(id), ang(ang) {}
    bool operator < ( const Seg &b ) const {
        if( u == b.u ){
            return cross( v - u, b.v - u ) < 0;
        }
        point aa = get_intersection( u, v, o, base );
        point bb = get_intersection( b.u, b.v, o, base );
        return aa.len() < bb.len();
    }
}L[mnx<<1];
bool cmp( const Seg &a, const Seg &b ){
    return a.ang < b.ang || ( a.ang == b.ang && a.in > b.in );
}
int n, all;
point p[mnx];
void add( point a, point b, int kk ){
    if( a < b ) swap( a, b );
    L[all++] = Seg( a, b, 1, kk, a.ang );
    L[all++] = Seg( b, a, 0, kk, b.ang );
}
double cal( const Seg &seg, double pre, double now ){
    point aa = get_intersection( seg.u, seg.v, o, point( cos(pre), sin(pre) ) );
    point bb = get_intersection( seg.u, seg.v, o, point( cos(now), sin(now) ) );
    return fabs( 0.5 * cross( aa, bb ) );
}
multiset<Seg> S;
multiset<Seg>::iterator itArr[mnx<<1];
void solve(){
    S.clear();
    double ans = 0, pre = -Pi;
    for( int i = 0; i < all; ++i ){
        double nowAng = L[i].ang;
        base = L[i].u;
        if( L[i].in ){
            if( !S.empty() )
                ans += cal( *S.begin(), pre, nowAng );
            itArr[L[i].id] = S.insert( L[i] );
        }
        else{
            ans += cal( *S.begin(), pre, nowAng );
            S.erase( itArr[L[i].id] );
        }
        pre = nowAng;
    }
    printf( "%.10lf\n", ans );
}
int main(){
    //freopen( "out.txt", "w", stdout );
    point c;
    while( scanf( "%lf%lf", &c.x, &c.y ) != EOF ){
        scanf( "%d", &n );
        for( int i = 0; i < n; ++i ){
            scanf( "%lf%lf", &p[i].x, &p[i].y );
            p[i] = p[i] - c, p[i].ang = atan2( p[i].y, p[i].x );
        }
        p[n] = p[0];
        all = 0;
        for( int i = 0, k = 0; i < n; ++i ){
            double d = fabs( p[i+1].ang - p[i].ang );
            if( d < Pi )
                add( p[i], p[i+1], k++ );
            else{
                point tmp = get_intersection( p[i], p[i+1], o, point( -1.0, 0 ) );
                tmp.ang = Pi * dcmp( p[i].ang );
                add( p[i], tmp, k++ );
                tmp.ang = Pi * dcmp( p[i+1].ang );
                add( tmp, p[i+1], k++ );
            }
        }
        sort( L, L + all, cmp );
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值