[裸题][扫描线] 求矩形面积并

大家都很强, 可与之共勉。

给出n 个矩形,求它们的面积并。
更准确一点,每个矩形将给出它的左上角和右下角的位置:x1; y1; x2; y2
这四个数都是整数且满足x1  x2; y1  y2.
Input
第1 行1 个整数:n,表示矩形的个数。
接下来n 行,每行4 个整数:x1 y1 x2 y2,表示一个矩形的左上角和右下角的坐标。
Output
输出area。
Sample
area.in
3
1 1 2 3
1 2 3 3
3 3 4 4
area.out
11
样例解释:一共有11 个点落在了上面三个矩形所表示的区域内:
(1; 1); (1; 2); (1; 3); (2; 1); (2; 2); (2; 3); (3; 2); (3; 3); (3; 4); (4; 3); (4; 4)

扫描线,对于一个举行(x1,y1,x2,y2),将它看成两个事件:在x1 这个时间将
(y1,y2) 这个区间加一,在x2+1 这个时间将(y1,y2) 这个区间减一。
这样,我们遍历整个时间,并在执行完这个时间的操作后看看有多少位置非0, 将其数量加到答案中,就完了,当然时间不能傻傻地一个一个枚,因为关键的时间点最多2n 个,其它时候面积是没有变的,所以要一段一段地算。至于怎么用线段树实现那么查看有多少个非零的位置,需要注意对于任何一个减一操作,前面一定有一个和它一样的加一操作,就只需要维护一下每个节点被完全覆盖的次数。再用另一个来统计子树中的那些修改导致这个节点还有
多少个非零。有点像标记永久化

#include "cctype"
#include "cstdio"
#include "algorithm"

const int MAXN = (int) 1e5 + 5;

const int Ins = 1, Del = -1;

typedef long long LL;

template <class T>
inline bool readIn(T &x)  {
    T opt = 1;
    char ch;
    while( !isdigit(ch = (char) getchar()) && (ch ^ -1) && (ch ^ 45) );
    if(ch == -1)  return false;
    if(ch == 45)  opt = -1, ungetc(ch, stdin);
    for(x = ch - 48; isdigit(ch = (char) getchar()); x = (x << 1) + (x << 3) + ch - 48);
    x *= opt;
}

struct Events  {
    int type, time, lf, rg;
    Events(int type = 0, int time = 0, int lf = 0, int rg = 0) : type(type), time(time), lf(lf), rg(rg) {  }
    inline bool operator < (const Events &rhs)  const  {
        return time < rhs.time;
    }
} events[MAXN << 1];

struct node  {
    int cnt, sum;
    node *ls, *rs;
    node(int cnt = 0, int sum = 0, node *ls = 0, node *rs = 0) : cnt(cnt), sum(sum), ls(ls), rs(rs) {  }
    inline int query(int lf, int rg)  { return cnt ? rg - lf + 1 : sum; }
    inline void update(int lf, int rg)  {
        int mid = (lf + rg) >> 1;
        sum = ls -> query(lf, mid) + rs -> query(mid + 1, rg);
    } 
} pool[MAXN << 1], *root, *tail = pool;

int n, x1, y1, x2, y2, tot;
LL ans;

node *build(int lf, int rg)  {
    node *nd = ++tail;
    if( lf == rg )  return nd;
    int mid = (lf + rg) >> 1;
    nd -> ls = build(lf, mid);
    nd -> rs = build(mid + 1, rg);  // alredy initialized;
    return nd;
}

void modify(node* &nd, int lf, int rg, int L, int R, int delta)  {
    if(L > lf || R < rg)  {
        int mid = (lf + rg) >> 1;
        if(L <= mid)  modify(nd -> ls, lf, mid, L, R, delta);
        if(R > mid)  modify(nd -> rs, mid + 1, rg, L, R, delta);
        nd -> update(lf, rg);
    }  else  nd -> cnt += delta;
    return;
}

int main()  {
    freopen("area.in", "r", stdin);  
    freopen("area.out", "w", stdout);
    readIn(n);
    for(register int i = 1; i <= n; ++i)  {
        readIn(x1), readIn(y1), readIn(x2), readIn(y2);
        events[tot++] = Events(1, x1, y1, y2); 
        events[tot++] = Events(-1, x2 + 1, y1, y2);
    }
    std::sort(events, events + tot);
    root = build(1, MAXN);
    for(register int i = 0, j; i < tot; i = j + 1)  {
        for(j = i; j < tot && events[j + 1].time == events[i].time; ++j);
            for(register int k = i; k <= j; ++k)
                modify(root, 1, MAXN, events[k].lf, events[k].rg, events[k].type);
        if(j ^ (tot - 1))  ans += (LL) root -> query(1, MAXN) * (events[j + 1].time - events[i].time);
    }
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(线段树,扫描线)