HUD 1828 Picture 矩形周长/线段树

题意:求重叠矩形的周长。
题解:枚举x区间时要注意求出, y 轴投影的线段数量。 即对应一个 x 有多少段不连续的线段,因为这关系的矩形的宽。 对 x 进行排列要记得入边在前,出边在后,否则边相交的两个矩形,就会把重边也计算在内。


#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;

#define L(x) ( x << 1 )  
#define R(x) ( x << 1 | 1 )  
#define N 20010

int y[N*2];

struct Node 
{
    int l, r, len, lc, rc, cover, cnt; /* lc, rc, 表示定的被覆盖的次数 */
} node[N*4];

struct Line
{
    int x, y1, y2, flag;
} line[N*2];

bool cmp ( Line a, Line b )
{
    if ( a.x != b.x )
        return a.x < b.x;
    return a.flag > b.flag; /* 当 x 相同时应当是入边在前, 画两个矩形分析分析 */
}

void y_update ( int u )
{
    if ( node[u].cover > 0 )
        node[u].len = y[node[u].r] - y[node[u].l];
    else if ( node[u].l + 1 == node[u].r )
        node[u].len = 0;
    else
        node[u].len = node[L(u)].len + node[R(u)].len;

    if ( node[u].cover > 0 )
        node[u].cnt = 1; /* cnt 记录投影到 y 轴上的线段数( 即不连续的段数) */
    else
    {
        node[u].cnt = node[L(u)].cnt + node[R(u)].cnt;
        if ( node[R(u)].lc != 0 && node[L(u)].rc != 0 )  
/*左子树的右端点如果跟右子树的左端点都被覆盖了,说明在整个区间里,有一个区间横跨左右子树,但被重复计算了,所以要减去1 */
            node[u].cnt--;
    }
}

void build( int u, int l, int r )
{
    node[u].l = l;   
    node[u].r = r;
    node[u].cnt = 0;
    node[u].len = node[u].cover = 0;
    node[u].lc = node[u].rc = 0;
    if ( l + 1 == r ) return;
    int mid = ( l + r ) >> 1;
    build ( L(u), l, mid );
    build ( R(u), mid, r );
}

void update ( int u, Line e )
{  
    if ( e.y1 == y[node[u].l] )
        node[u].lc += e.flag;
    if ( e.y2 == y[node[u].r] )
        node[u].rc += e.flag;
    if ( e.y1 == y[node[u].l] && e.y2 == y[node[u].r] )
    {
        node[u].cover += e.flag; 
        y_update ( u );
        return;
    }

    if ( e.y1 >= y[node[R(u)].l] )
        update ( R(u), e );
    else if ( e.y2 <= y[node[L(u)].r] )
        update ( L(u), e );
    else
    {
        Line temp1 = e;
        Line temp2 = e;
        temp1.y2 = y[node[L(u)].r];
        temp2.y1 = y[node[R(u)].l];
        update ( L(u), temp1 );
        update ( R(u), temp2 );
    }
    y_update ( u );
}

int main()
{
    //freopen ( "a.txt", "r", stdin );
    int n, i, k, x1, y1, x2, y2;
    while ( scanf("%d",&n) != EOF )
    {
        for ( i = k = 1; i <= n; i++, k++ )
        {
            scanf("%d %d %d %d", &x1, &y1, &x2, &y2 );
            line[k].x = x1;
            line[k].y1 = y1;
            line[k].y2 = y2;
            line[k].flag = 1;
            y[k] = y1;
            k++;
            line[k].x = x2;
            line[k].y1 = y1;
            line[k].y2 = y2;
            line[k].flag = -1;
            y[k] = y2;
        }

        sort ( line+1, line+k, cmp );
        sort ( y+1, y+k );
        build ( 1, 1, k-1 );

        update ( 1, line[1] );
        int ans = node[1].len;
        int temp = node[1].len;
        int tcnt = node[1].cnt;

        for ( i = 2; i < k; i++ )
        {
            update ( 1, line[i] );
            ans += abs ( node[1].len - temp );
            ans += 2 * ( line[i].x - line[i-1].x ) * tcnt;
            temp = node[1].len;
            tcnt = node[1].cnt;
        }
        printf ( "%d\n", ans );
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值