[USACO5.5]Picture --- 线段树 + 扫描线

传送门洛谷P1856


题目描述

墙上贴着许多形状相同的海报、照片。它们的边都是水平和垂直的。每个矩形图片可能部分或全部的覆盖了其他图片。所有矩形合并后的边长称为周长。


分析

矩形的周长并,注意线段的排序:坐标相同是,入边优先


代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 , k = 1;

    for(; '0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;

    for(; '0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';

    return sum * k;
}

struct segment
{
    int x, y1, y2, k;
    IL segment(int x_ = 0, int y1_ = 0, int y2_ = 0, int k_ = 0)
    {
        x = x_; y1 = y1_; y2 = y2_; k = k_;
    }
    friend bool operator < (const segment &a, const segment &b)
    {
        if(a.x == b.x) return a.k > b.k;
        return a.x < b.x;
    }
}seg[10005];

struct node
{
    node *lch, *rch;
    int cnt, len, cov;
    bool covl, covr;
    IL node()
    {
        lch = rch = 0;
        cnt = len = cov = 0;
        covl = covr = 0;
    }
} *root;

int n, m;
int num[10005];

IL int abs_(int x) { return x >= 0 ? x : -x; }

IL void build(node *p, int l, int r)
{
    if(l == r) return ;
    int mid = (l + r) >> 1;
    p->lch = new node; build(p->lch, l, mid);
    p->rch = new node; build(p->rch, mid + 1, r);
}

IL void pushup(node *p, int l, int r)
{
    if(p->cnt > 0)
    {
        p->covl = p->covr = 1;
        p->cov = 1;
        p->len = num[r + 1] - num[l];
    }else
    if(l == r)
    {
        p->covl = p->covr = 0;
        p->cov = 0;
        p->len = 0;
    }else
    {
        p->covl = p->lch->covl; p->covr = p->rch->covr;
        p->cov = p->lch->cov + p->rch->cov - (p->lch->covr & p->rch->covl);
        p->len = p->lch->len + p->rch->len;
    }
}

IL void update(node *p, int l, int r, int x, int y, int k)
{
    if(l == x && r == y)
    {
        p->cnt += k;
        pushup(p, l ,r);
        return ;
    }
    int mid = (l + r) >> 1;
    if(y <= mid) update(p->lch, l, mid, x, y, k); else
    if(mid < x) update(p->rch, mid + 1, r, x, y, k); else
    {
        update(p->lch, l, mid, x, mid, k);
        update(p->rch, mid + 1, r , mid + 1, y, k);
    } 
    pushup(p, l, r);
}

int main()
{
    n = read();
    for(int i = 1, k = 0, x1, y1, x2, y2; i <= n; ++i)
    {
        x1 = read(); y1 = read(); x2 = read(); y2 = read();
        seg[++k] = segment(x1, y1, y2, 1);
        seg[++k] = segment(x2, y1, y2, -1);
        num[++m] = y1; num[++m] = y2;
    }
    n <<= 1;
    sort(seg + 1,seg + n + 1);
    sort(num + 1, num + m + 1);
    m = unique(num + 1, num + m + 1) - (num + 1);

    root = new node;
    build(root, 1, m - 1);

    int ans = 0;
    ++n;
    for(int i = 1, pre = 0, y1, y2; i <= n; ++i)
    {
        ans += (seg[i].x - seg[i - 1].x) * (root->cov) << 1;
        ans += abs_(root->len - pre); pre = root->len;
        if(i < n)
        {
            y1 = lower_bound(num + 1, num + m + 1, seg[i].y1) - num;
            y2 = lower_bound(num + 1, num + m + 1, seg[i].y2) - num - 1;
            update(root, 1, m - 1, y1, y2, seg[i].k);
        }
    }

    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值