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

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

Picture

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2902    Accepted Submission(s): 1533


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

—————————————————————分割線

————————————————————

題目大意:

給你n個矩形,保證這些矩形都是水平或者垂直的,求這些矩形所圍成圖形的周長

思路:

結構體儲存矩形的左右端點,高度,下底邊為1,上底邊為-1

對矩形的高度從小到大排序,用一根掃描線從下往上掃

1、求底邊:

周長並是長加寬,對於面積並中,已經知道每次覆蓋區間線段長度,那麼只要儲存上一次的長度,那麼這次和上一次的差值就是每次增加的線段長。即是底邊

2、求高:

已知底邊的長度,那麼只要記錄底邊是由多少條連續區間線段構成的,假如為m,那麼寬的數量就是m*2了。畫畫圖

那麼怎麼求得連續子區間的數量?

一個節點的連續子區間的數量=左二子的連續區間數量+右兒子連續區間數量-(中間區間是否相連)

怎麼判斷中間區間是否相連? 記錄一個區間的左右端點是否存在,如果左二子的右端點和右兒子的左端點都存在,則表明中間是連續的

仍然點表示線段,更新線段樹的時候右端點-1,計算的時候把線段變為點  所以len[rt]=r-l+1

程式碼中儲存節點資訊的陣列並沒有初始化,因為根本沒必要,是為什麼呢?想想就知道了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=20001;
using namespace std;
int cnt[maxn<<2],len[maxn<<2],numseg[maxn<<2];
bool lbd[maxn<<2],rbd[maxn<<2];
struct Seg
{
    int l,r,h;
    int s;
    Seg(){};
    Seg(int a,int b,int c,int d):l(a),r(b),h(c),s(d){};
    bool operator<(const Seg&cmp)const{
        return h<cmp.h;
    }
}ss[maxn];
void push_up(int rt,int l,int r)
{
    if(cnt[rt]){
        len[rt]=r-l+1;
        numseg[rt]=lbd[rt]=rbd[rt]=1;
    }
    else if(l==r){
        len[rt]=numseg[rt]=lbd[rt]=rbd[rt]=0;
    }
    else {
        lbd[rt]=lbd[rt<<1];
        rbd[rt]=rbd[rt<<1|1];
        len[rt]=len[rt<<1]+len[rt<<1|1];
        numseg[rt]=numseg[rt<<1]+numseg[rt<<1|1]-lbd[rt<<1|1]*rbd[rt<<1];
    }
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(L<=l&&r<=R){
        cnt[rt]+=c;
        push_up(rt,l,r);
        return ;
    }
    int m=(l+r)>>1;
    if(L<=m) update(L,R,c,lson);
    if(m<R) update(L,R,c,rson);
    push_up(rt,l,r);
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF){
        int a,b,c,d,m=0;
        int l_bd=1<<30,r_bd=-1<<30;
        while(n--){
            scanf("%d %d %d %d",&a,&b,&c,&d);
            l_bd=min(l_bd,a),r_bd=max(r_bd,c);
            ss[m++]=Seg(a,c,b,1);
            ss[m++]=Seg(a,c,d,-1);
        }
        sort(ss,ss+m);
        int ret=0,last=0;
        for(int i=0;i<m;++i){
            update(ss[i].l,ss[i].r-1,ss[i].s,l_bd,r_bd,1);
            ret+=numseg[1]*(ss[i+1].h-ss[i].h)*2;
            ret+=abs(len[1]-last);
            last=len[1];
        }
        printf("%d\n",ret);
    }
    return 0;
}


因為一個樣例結束後,是計算到上底邊的,下個樣例開始時是從下底邊開始計算的,那麼 -1+1=0,就清空了,節點資訊是實時更新的。