1. 程式人生 > >[USACO5.5]矩形周長Picture[掃描線+線段樹]

[USACO5.5]矩形周長Picture[掃描線+線段樹]

inline print operator sta ans 左右 -c con new

題意:給出一些矩陣,求這些矩陣合並後外部(被包括在內部的不算)周長

端點-1這個是用點代替了邊,區間內有幾個點就代表區間長度是多少

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
inline void chmax(int &x, int y) {if (x < y) x = y;}
inline void chmin(int &x, int y) {if (x > y) x = y;}
int Max = -inf, Min = inf, n, m, tot, ans, lastlen, x, y, xx, yy;
struct Edge {
    int l, r, h, v;
    inline bool operator < (const Edge & rhs) const {
        return h == rhs.h ? v > rhs.v : h < rhs.h;
    }
} G[10005];
inline void add(int l, int r, int h, int v) {
    G[++tot] = (Edge) {l, r, h, v};
}
struct Node {
    int sum, cnt, len;
    //sum區間整體覆蓋次數,cnt有幾條不相交的線段覆蓋了這個區間
    //len區間內被覆蓋的長度
    bool rcvd, lcvd;//左右端點是否被覆蓋
    Node *ls, *rs;
    inline void pushup(int l, int r) {
        if (sum) cnt = 1, len = r-l+1, lcvd = rcvd = 1;
        else if (l == r) len = 0, cnt = 0, lcvd = rcvd = 0;//Attention
        else {
            len = ls->len + rs->len;
            cnt = ls->cnt + rs->cnt;
            if (ls->rcvd && rs->lcvd) --cnt;
            lcvd = ls->lcvd;
            rcvd = rs->rcvd;
        }
    }
} pool[100005], *root;
void add(Node *cur, int l, int r, int ql, int qr, int v) {
    if (ql <= l && r <= qr) return cur->sum += v, cur->pushup(l, r);
    int mid = l+r>>1;
    if (ql <= mid) add(cur->ls, l, mid, ql, qr, v);
    if (qr >  mid) add(cur->rs, mid+1, r, ql, qr, v);
    cur->pushup(l, r);
}
inline Node *newNode() {
    static int cnt = 0;
    return &(pool[cnt++]);
}
Node *build(int l, int r) {
    Node *cur = newNode();
    if (l == r) return cur;
    int mid = l+r>>1;
    cur->ls = build(l, mid), cur->rs = build(mid+1, r);
    return cur;
}

int main(void) {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d%d%d%d", &x, &y, &xx, &yy);
        chmax(Max, xx), chmin(Min, x), add(x, xx, y, 1), add(x, xx, yy, -1);
    }
    if (Min <= 0) {//把坐標轉為正數,其實直接加上10001(本題坐標最大值)也是可以的
        for(int i = 1; i <= tot; ++i) G[i].l += -Min+1, G[i].r += -Min+1;
        Max -= Min;
    }
    root = build(1, Max);
    sort(G+1, G+1+tot);
    for(int i = 1; i <= tot; ++i) {
        add(root, 1, Max, G[i].l, G[i].r-1, G[i].v);//不是很懂為什麽要-1,這奇怪的方式
        while(G[i].h==G[i+1].h&&G[i].v==G[i+1].v) {//防止多次統計答案
            ++i;
            add(root, 1, Max, G[i].l, G[i].r-1, G[i].v);
        }
        ans += abs(root->len-lastlen);//統計橫邊長度
        lastlen = root->len;
        ans += root->cnt*2*(G[i+1].h-G[i].h);//統計豎邊長度
    }
    printf("%d\n", ans);
    return 0;
}

[USACO5.5]矩形周長Picture[掃描線+線段樹]