线段树扫描线

我的博客:http://startcraft.cn

矩形面积并

如图,考虑求图中矩形的面积并

这个问题可以转换成求下图中这些部分的面积

就是从下往上扫一遍,遇到矩形的下边就加入,然后统计当前的线段长度,当然重叠部分的线段只算一次长度,遇到上边就删除,然后每种颜色的矩形面积就是 当前线段长度*(将要加入的线段的高度-上一条加入的线段的高度)
然后这些累加起来就是矩形的面积并
现在的问题就是如何维护当前线段的长度
这里就要用到线段树了
先对矩形的边进行处理,按高度排序,然后下边标记为1,上边标记为-1,然后从下往上扫描
结点信息:

struct node
{
    int len;//当前区间的被覆盖的线段的长度
    int s;//当前区间被完全覆盖的次数
    int l;//当前区间的左端点
    int r;//当前区间的右端点
};

让后当前这个线段树的叶子结点是l+1=r,而不是传统的l=r,因为l=r是一个点这对矩形没有意义
构造线段树

void build (int l, int r, int id)
{
    tree[id].len = 0;
    tree[id].s = 0;
    tree[id].l = l;
    tree[id].r = r;
    if (l + 1 == r)//叶子结点
        return ;
    int mid = (l + r) >> 1;
    build(l, mid, id << 1);
    build(mid, r, id << 1 | 1);//不是mid+1,不然会导致一些区间无法覆盖
}

这样构造出来的线段树是长这样的

然后是push_up()函数

void push_up(int id)
{
    if (tree[id].s)//该区间被完全覆盖则长度就是区间的长度
        tree[id].len = r-l
    else//否则就是左区间的长度加右区间的长度
        tree[id].len = tree[id << 1].len + tree[id << 1 | 1].len;
}

有一点很重要,区间是否被完全覆盖是区间自己的事情,即不从子节点更新,也就是说[1,2],[2,3]被覆盖不代表[1,3]被覆盖,但是长度会从子节点更新,所以长度和被完全覆盖是一样的,答案不会错误,换句话说就是结点的s的值不从子节点更新,s的值只会在更新时被自己更新,所以虽然是区间更新,但与传统的区间更新有很大不同,也就不需要lazy tag和push_down
update

void update(int id, int L, int R, int number)
{
    if (tree[id].l >= L && tree[id].r <= R)
    {
        tree[id].s += number;
	/*如果是下边就是1,代表该区间被完全覆盖了,如果是上边就是-1,覆盖次数就减了一次
	s的值不会小于0,因为下边总是先扫描到*/
        push_up(id);//计算当前区间的线段长度
        return ;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (mid > L)
        update(id << 1, L, R, number);
    if (mid < R)
        update(id << 1 | 1, L, R, number);
    push_up(id);
}

查询的话,因为只需要知道总的线段的长度,所以只要tree[1].len

例题 HDU-1542

AC代码:这题的坐标有小数,所以需要离散化

#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
#define wfor(i,j,k) for(i=j;i<k;++i)
#define mfor(i,j,k) for(i=j;i>=k;--i)
// void read(int &x) {
//  char ch = getchar(); x = 0;
//  for (; ch < '0' || ch > '9'; ch = getchar());
//  for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
// }
const int maxn = 2005;
struct node
{
    double len;
    int s;
    int l;
    int r;
};
node tree[maxn << 2];
struct Line
{
    int l;
    int r;
    int high;
    int flag;
    bool operator <(const Line other)const
    {
        return high < other.high;
    }
};
struct Point
{
    double x1;
    double y1;
    double x2;
    double y2;
};
Point point[maxn];
vector <double>num;
Line line[maxn << 2];
void push_up(int id)
{
    if (tree[id].s)
        tree[id].len = num[tree[id].r - 1] - num[tree[id].l - 1];
    else
        tree[id].len = tree[id << 1].len + tree[id << 1 | 1].len;
}
void build (int l, int r, int id)
{
    tree[id].len = 0;
    tree[id].s = 0;
    tree[id].l = l;
    tree[id].r = r;
    if (l + 1 == r)
    {
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, id << 1);
    build(mid, r, id << 1 | 1);
}
void update(int id, int L, int R, int number)
{
    if (tree[id].l >= L && tree[id].r <= R)
    {
        tree[id].s += number;
        push_up(id);
        return ;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (mid > L)
        update(id << 1, L, R, number);
    if (mid < R)
        update(id << 1 | 1, L, R, number);
    push_up(id);
}
int main()
{
    std::ios::sync_with_stdio(false);
    int n;
    int casecnt = 0;
    while (cin >> n && n)
    {
        num.clear();
        casecnt++;
        int i;
        wfor(i, 0, n)
        {
            cin >> point[i].x1 >> point[i].y1 >> point[i].x2 >> point[i].y2;
            num.push_back(point[i].x1);
            num.push_back(point[i].x2);
            num.push_back(point[i].y1);
            num.push_back(point[i].y2);
        }
        sort(num.begin(), num.end());
        auto it = unique(num.begin(), num.end());
        int len = it - num.begin();
        int cnt = 0;
        wfor(i, 0, n)
        {
            int x1, y1, x2, y2;
            x1 = lower_bound(num.begin(), it, point[i].x1) - num.begin() + 1;
            x2 = lower_bound(num.begin(), it, point[i].x2) - num.begin() + 1;
            y1 = lower_bound(num.begin(), it, point[i].y1) - num.begin() + 1;
            y2 = lower_bound(num.begin(), it, point[i].y2) - num.begin() + 1;
            line[cnt].l = x1;
            line[cnt].r = x2;
            line[cnt].flag = 1;
            line[cnt++].high = y1;
            line[cnt].l = x1;
            line[cnt].r = x2;
            line[cnt].flag = -1;
            line[cnt++].high = y2;
        }
        sort(line, line + cnt);
        build(1, len, 1);
        update(1, line[0].l, line[0].r, line[0].flag);
        int last = line[0].high;
        double ans = 0;
        wfor(i, 1, cnt)
        {
            ans += tree[1].len * (num[line[i].high - 1] - num[last - 1]);
            update(1, line[i].l, line[i].r, line[i].flag);
            last = line[i].high;
        }
        cout << "Test case #" << casecnt << endl;
        cout << "Total explored area: ";
        cout << fixed << setprecision(2) << ans << endl;
        cout << endl;
    }
    return 0;
}

重叠矩形的周长

可以和计算面积一样的思路,对于所有的横边扫描一次,周长就是当前的线段长度与上一次的线段长度之差的绝对值
然后对竖着的边也做一次就是周长

例题 poj-1177

#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
#define wfor(i,j,k) for(i=j;i<k;++i)
#define mfor(i,j,k) for(i=j;i>=k;--i)
// void read(int &x) {
//  char ch = getchar(); x = 0;
//  for (; ch < '0' || ch > '9'; ch = getchar());
//  for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
// }
const int maxn = 20005;
struct node
{
    int l, r;
    int s;
    int len;
};
struct Line
{
    int l, r;
    int high;
    int flag;
    bool operator <(const Line & other)const
    {
        return high < other.high;
    }
};
vector <int>num;
struct Rect
{
    int x1;
    int y1;
    int x2;
    int y2;
};
Rect rec[maxn];
Line line1[maxn];
Line line2[maxn];
node tree[maxn << 2];
void build (int l, int r, int id)
{
    tree[id].l = l;
    tree[id].r = r;
    tree[id].len = 0;
    tree[id].s = 0;
    if (l + 1 == r)
        return ;
    int mid = (l + r) >> 1;
    build(l, mid, id << 1);
    build(mid, r, id << 1 | 1);
}
void push_up(int id)
{
    if (tree[id].s)
        tree[id].len = num[tree[id].r - 1] - num[tree[id].l - 1];
    else
        tree[id].len = tree[id << 1].len + tree[id << 1 | 1].len;
}
void update(int id, int L, int R, int number)
{
    if (tree[id].l >= L && tree[id].r <= R)
    {
        tree[id].s += number;
        push_up(id);
        return ;
    }
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (mid > L)
        update(id << 1, L, R, number);
    if (mid < R)
        update(id << 1 | 1, L, R, number);
    push_up(id);
}
int main()
{
    std::ios::sync_with_stdio(false);
    int n;
    cin >> n;
    int i;
    wfor(i, 0, n)
    {
        cin >> rec[i].x1 >> rec[i].y1 >> rec[i].x2 >> rec[i].y2;
        num.push_back(rec[i].x1);
        num.push_back(rec[i].x2);
        num.push_back(rec[i].y1);
        num.push_back(rec[i].y2);
    }
    sort(num.begin(), num.end());
    vector<int>::iterator it = unique(num.begin(), num.end());
    int len = it - num.begin();
    int cnt = 0;
    wfor(i, 0, n)
    {
        rec[i].x1 = lower_bound(num.begin(), it, rec[i].x1) - num.begin() + 1;
        rec[i].x2 = lower_bound(num.begin(), it, rec[i].x2) - num.begin() + 1;
        rec[i].y1 = lower_bound(num.begin(), it, rec[i].y1) - num.begin() + 1;
        rec[i].y2 = lower_bound(num.begin(), it, rec[i].y2) - num.begin() + 1;
    }
    wfor(i, 0, n)
    {
        line1[cnt].l = rec[i].x1;
        line1[cnt].r = rec[i].x2;
        line1[cnt].high = rec[i].y1;
        line1[cnt].flag = 1;
        line2[cnt].l = rec[i].y1;
        line2[cnt].r = rec[i].y2;
        line2[cnt].high = rec[i].x1;
        line2[cnt++].flag = 1;
        line1[cnt].l = rec[i].x1;
        line1[cnt].r = rec[i].x2;
        line1[cnt].high = rec[i].y2;
        line1[cnt].flag = -1;
        line2[cnt].l = rec[i].y1;
        line2[cnt].r = rec[i].y2;
        line2[cnt].high = rec[i].x2;
        line2[cnt++].flag = -1;
    }
    sort(line1, line1 + cnt);
    sort(line2, line2 + cnt);
    ll ans = 0;
    build(1, len, 1);
    update(1, line1[0].l, line1[0].r, line1[0].flag);
    ans += num[line1[0].r - 1] - num[line1[0].l - 1];
    int last = ans;
    wfor(i, 1, cnt)
    {
        update(1, line1[i].l, line1[i].r, line1[i].flag);
        ans += abs(tree[1].len - last);
        last = tree[1].len;
    }
    build(1, len, 1);
    update(1, line2[0].l, line2[0].r, line2[0].flag);
    ans += num[line2[0].r - 1] - num[line2[0].l - 1];
    last = num[line2[0].r - 1] - num[line2[0].l - 1];
    wfor(i, 1, cnt)
    {
        update(1, line2[i].l, line2[i].r, line2[i].flag);
        ans += abs(tree[1].len - last);
        last = tree[1].len;
    }
    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值