hdu4052——线段树,矩形并

题意:在一个给定大小的平面上给出一些不相交的矩形,在这个平面放一个长度为M的线段且不与任何一个矩形相交,有多少种方法。

不失一般性的,可以先求横着放线段的情况。如果枚举放线段的起点的话,那么每个矩形前方m-1长度与自己等宽的区域也是不能放置起点的,那么除去所有不能放置起点的区域,其余区域的面积就是横着放线段的方案数。竖着放同理。

求面积,上模板,线段树求矩形并的面积。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <map>
#define ls(x) (x << 1)
#define rs(x) ((x << 1) + 1)
using namespace std;

const int maxn = 100000 + 100;
int w, h, n, m, tot;
map<int, int> mm;
int num[maxn];

struct rec
{
    int x1, x2, y1, y2;
}mac[maxn];

struct line
{
    int a, b, c;
    int flag;
    bool operator < (const line & rhs) const
    {
        return c < rhs.c;
    }
}lines[maxn];

struct seg_tree
{
    int l[5 * maxn], r[5 * maxn];
    int c[5 * maxn];
    int ans[5 * maxn];
    
    void build(int pos, int le, int ri)
    {
        l[pos] = le; r[pos] = ri;
        c[pos] = 0;
        ans[pos] = num[ri] - num[le];
        if(ri - le > 1)
        {
            int mid = (le + ri) / 2;
            build(ls(pos), le, mid);
            build(rs(pos), mid, ri);
        }
    }
    
    void update(int pos)
    {
        if(c[pos] > 0) ans[pos] = 0;
        else if(r[pos] - l[pos] == 1) ans[pos] = num[r[pos]] - num[l[pos]];
        else ans[pos] = ans[ls(pos)] + ans[rs(pos)];
    }
    
    void add(int pos, int le, int ri, int flag)
    {
        if(le <= l[pos] && ri >= r[pos])
        {
            if(flag) c[pos]++;
            else c[pos]--;
        }
        else
        {
            int mid = (l[pos] + r[pos]) / 2;
            if(ri > mid) add(rs(pos), le, ri, flag);
            if(le < mid) add(ls(pos), le, ri, flag);
        }
        update(pos);
    }
    
}tree;

void prework()
{
    for(int i = 0; i < n; ++i)
    {
        scanf("%d %d %d %d", &mac[i].x1, &mac[i].y1, &mac[i].x2, &mac[i].y2);
    }
}

void solve()
{
    long long ans = 0;
    mm.clear(); mm[1] = 0; mm[h + 1] = 0; mm[h - m + 2] = 0;
    for(int i = 0; i < n; ++i)
    {
        int tmp = (mac[i].y1 - (m - 1)) > 1 ? mac[i].y1 - (m - 1) : 1; 
        lines[i].a = lines[i + n].a = tmp;
        lines[i].b = lines[i + n].b = mac[i].y2 + 1;
        lines[i].c = mac[i].x1; lines[i + n].c = mac[i].x2 + 1;
        lines[i].flag = 1; lines[i + n].flag = 0;
        mm[lines[i].a] = 0; mm[lines[i].b] = 0;
    }
    tot = 0;
    for(map<int, int>::iterator it = mm.begin(); it != mm.end(); ++it)
    {
        it->second = ++tot;
        num[tot] = it->first;
    }
    sort(lines, lines + 2 * n);
    tree.build(1, 1, tot);
    if(m != 1) tree.add(1, mm[h - m + 2], mm[h + 1], 1);
    int sl = 1;
    for(int i = 0; i < 2 * n; ++i)
    {
        ans += (long long)(lines[i].c - sl) * tree.ans[1];
        tree.add(1, mm[lines[i].a], mm[lines[i].b], lines[i].flag);
        sl = lines[i].c;
    }
    ans += (long long)((w + 1) - sl) * tree.ans[1];
    mm.clear(); mm[1] = 0; mm[w + 1] = 0; mm[w - m + 2] = 0;
    for(int i = 0; i < n; ++i)
    {
        int tmp = (mac[i].x1 - (m - 1) > 1) ? mac[i].x1 - (m - 1) : 1;
        lines[i].a = lines[i + n].a = tmp;
        lines[i].b = lines[i + n].b = mac[i].x2 + 1;
        lines[i].c = mac[i].y1; lines[i + n].c = mac[i].y2 + 1;
        lines[i].flag = 1; lines[i + n].flag = 0;
        mm[lines[i].a] = 0; mm[lines[i].b] = 0;
    }
    tot = 0;
    for(map<int, int>::iterator it = mm.begin(); it != mm.end(); ++it)
    {
        it->second = ++tot;
        num[tot] = it->first;
    }
    sort(lines, lines + 2 * n);
    tree.build(1, 1, tot);
    if(m != 1) tree.add(1, mm[w - m + 2], mm[w + 1], 1);
    sl = 1;
    for(int i = 0; i < 2 * n; ++i)
    {
        ans += (long long)(lines[i].c - sl) * tree.ans[1];
        tree.add(1, mm[lines[i].a], mm[lines[i].b], lines[i].flag);
        sl = lines[i].c;
    }
    ans += (long long)((h + 1) - sl) * tree.ans[1];
    if(m == 1) ans /= 2;
    cout << ans << "\n";
}

int main()
{
    freopen("in.txt", "r", stdin);
    while(~scanf("%d %d %d %d", &w, &h, &n, &m))
    {
        prework();
        solve();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值