1. 程式人生 > >HDU 1828 Picture(線段樹掃描線·周長並)

HDU 1828 Picture(線段樹掃描線·周長並)

題意  給你一些矩形的左下和右上的座標  求這些矩形的周長並

也先來看點圖

  

和麵積並類似  求周長並也可以對每條豎邊從左往右進行掃描  每次周長增加了多少呢 可以發現y方向上對周長增加的量就是掃描線上線段的總長度的改變數  x方向增加了線段段數 * 2 倍的與下一條豎邊間的距離  因為每一段都會對應兩個橫邊

那麼我們需要維護線段的總長度len和線段的段數num  len和麵積並的是一樣的  num有點類似區間合併  由子節點更新父節點的時候  先讓父節點的num等於兩個子節點的num的和  但是當左孩子結點的右端有線段  而其右孩子的左端有線段的時候  這兩段是可以連在一起的  父節點的num就要減去1  所以我們還要維護每個區間左端是否有線段lh  右邊是否有線段rh

另外求周長並還要注意當兩個豎邊的x值相等時  要把入邊放前面  出邊放後面  也就是排序時要注意一下  否則會多加上一些長度

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10005, M = N * 4;

struct SLine
{
    int x, y1, y2, flag;
    SLine() {}
    SLine(int xx, int a, int b, int f):
        x(xx), y1(a), y2(b), flag(f) {}
    bool operator< (const SLine &s) const
    {
        if(x == s.x) return flag > s.flag;
        //有入邊出邊在一條線上時 要先掃描入邊 不然周長可能會多算重疊部分
        return x < s.x;
    }
} line[N];

int num[M], cnt[M], len[M], y[N];
bool lh[M], rh[M];

void pushup(int p, int s, int e)
{
    if(cnt[p]) //cnt == 0 時才需要由子節點來更新父節點
    {
        len[p] = y[e] - y[s - 1];
        num[p] = lh[p] = rh[p] = 1;
    }
    else if(s == e)
        len[p] = num[p] = lh[p] = rh[p] = 0;
    else  //區間合併
    {
        len[p] = len[p << 1] + len[p << 1 | 1];
        num[p] = num[p << 1] + num[p << 1 | 1];
        lh[p] = lh[p << 1], rh[p] = rh[p << 1 | 1];
        if(rh[p << 1] && lh[p << 1 | 1]) --num[p];
    }
}

void update(int p, int s, int e, int l, int r, int v)
{
    if(l <= s && e <= r)
    {
        cnt[p] += v;
        pushup(p, s, e);
        return;
    }
    int mid = (s + e) >> 1;
    if(l <= mid) update(p << 1, s, mid, l, r, v);
    if(r > mid) update(p << 1 | 1, mid + 1, e, l, r, v);
    pushup(p, s, e);
}

int main()
{
    int n, m, x1, y1, x2, y2;
    while(~scanf("%d", &n))
    {
        for(int i = m = 0; i < n; ++i)
        {
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            y[m] = y1, y[m + 1] = y2;
            line[m++] = SLine(x1, y1, y2, 1);
            line[m++] = SLine(x2, y1, y2, -1);
        }
        sort(line, line + m);
        sort(y, y + m);  //離散化

        int ans = 0, last = 0, l, r;
        for(int i = 0; i < m; ++i)
        {
            l = lower_bound(y, y + m, line[i].y1) - y + 1;
            r = lower_bound(y, y + m, line[i].y2) - y;
            update(1, 1, m, l, r, line[i].flag);
            if(i < m - 1) ans += 2 * num[1] * (line[i + 1].x - line[i].x); //橫邊
            ans += abs(len[1] - last); //豎邊
            last = len[1];
        }
        printf("%d\n", ans);
    }
    return 0;
}

Picture


Problem Description A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of all rectangles is called the perimeter. 

Write a program to calculate the perimeter. An example with 7 rectangles is shown in Figure 1. 



The corresponding boundary is the whole set of line segments drawn in Figure 2. 



The vertices of all rectangles have integer coordinates.
Input Your program is to read from standard input. The first line contains the number of rectangles pasted on the wall. In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate. 

0 <= number of rectangles < 5000 
All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.

Please process to the end of file.
Output Your program is to write to standard output. The output must contain a single line with a non-negative integer which corresponds to the perimeter for the input rectangles.
Sample Input 7 -15 0 5 10 -5 8 20 25 15 -4 24 14 0 -6 16 4 2 15 10 22 30 10 36 20 34 0 40 16
Sample Output 228