線段樹初步(3)
今天我們學習掃描線。掃描線大多數時候用於計算矩形面積,那我們如何處理有重合的情況呢?
我們想象有一條線從下向上掃描,之後每次掃描的時候,你可以計算一下當前在線上有多長的區間被覆蓋,我們相當於把矩形轉化為無數小段,之後分別進行區間求和。這一部分是可以用線段樹實現的。
這裏有一些問題,首先因為有重疊的區間,所以我們需要記錄每個區間被覆蓋過多少次,這樣可以避免在刪除邊的時候計算面積出錯。我們可以在一開始對邊賦值,入邊為1,出邊為-1,每次進行區間修改的時候相應的修改每個區間的值。如果區間當前的值>=1,就說明當前區間已經被完全覆蓋,如果是0則沒有被完全覆蓋。註意在每次返回和修改的時候都要先修改區間值,在依據相應的情況修改區間總和。
如何優化呢?在每條兩條線之間是沒有變化的,所以這部分的矩形可以直接用width*length的方法計算出面積之後加上即可。這樣只要枚舉每一條線就好了。
還有就是要註意因為計算的是區間,所以每次傳參要區間首++。
最後就是這個題與普通區間修改不同,他不需要lazy,他也不需要下放任何標記,因為你如果不需要修改子區間的話他就不需要下放,而如果你修改了已經覆蓋的區間之中的一小段區間,那麽對於大區間並沒有任何影響。
總的來說掃描線的基本應用還是很簡單的,不過細節很多……
看一下題吧!
題目描述
在2051年,若幹火星探險隊探索了這顆紅色行星的不同的區域並且制作了這些區域的地圖。
現在, Baltic空間機構有一個雄心勃勃的計劃:他們想制作一張整個行星的地圖。為了考慮必要的工作,他們需要知道地圖上已經存在的全部區域的大小。你的任務是寫一個計算這個區域大小的程序。
任務:計算地圖覆蓋的全部的區域。
輸入輸出格式
輸入格式:
輸入文件的第一行包含一個整數N(1<=N<=10000),表示可得到的地圖數目。
以下N行,每行描述一張地圖。
每行包含4個整數x1,y1,x2和y2(0<=x1<x2<=30000,0<=y1<y2<=30 000)。數值(x1,y1)和(x2,y2)是坐標,分別表示繪制區域的左上角和右下角坐標。每張地圖是矩形的,並且它的邊是平行與X坐標軸或Y坐標軸的。
輸出格式:
包含一個整數,表示探索區域的總面積(即所有矩形的公共面積)。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cmath> #define rep(i,a,n) for(ll i = a;i <= n;i++) #define per(i,n,a) for(ll i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; typedef long long ll; const ll M = 300005; struct seg { ll v,w; }t[M*4]; ll n,ans,cnt,a,b,c,d; struct lines { ll begin,end,high,x; }l[M]; bool cmp(lines a,lines b) { return a.high < b.high; } ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op; } void build(ll p,ll l,ll r) { t[p].v = t[p].w = 0; if(l == r) return; ll mid = (l+r) >> 1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } void modify(ll p,ll l,ll r,ll kl,ll kr,ll q) { if(l == kl && r == kr) { t[p].w += q; if(t[p].w == 0) t[p].v = t[p<<1].v + t[p<<1|1].v; else t[p].v = r-l+1; // else t[p].v = 0; return; } ll mid = (l+r) >> 1; if(kr <= mid) modify(p<<1,l,mid,kl,kr,q); else if(kl > mid) modify(p<<1|1,mid+1,r,kl,kr,q); else modify(p<<1,l,mid,kl,mid,q),modify(p<<1|1,mid+1,r,mid+1,kr,q); if(t[p].w == 0) t[p].v = t[p<<1].v + t[p<<1|1].v; else t[p].v = r-l+1; } int main() { n = read(); build(1,0,M); rep(i,1,n) { a = read(),b = read(),c = read(),d = read(); l[++cnt].begin = a,l[cnt].end = c,l[cnt].high = b,l[cnt].x = 1; l[++cnt].begin = a,l[cnt].end = c,l[cnt].high = d,l[cnt].x = -1; } sort(l+1,l+1+cnt,cmp); // rep(i,1,cnt) prllf("%d %d %d %d\n",l[i].begin,l[i].end,l[i].high,l[i].x); rep(i,1,cnt) { ll h = l[i].high - l[i - 1].high; ans += t[1].v * h; modify(1,0,M,l[i].begin+1,l[i].end,l[i].x); // printf("%d\n",t[1].v); } printf("%lld\n",ans); return 0; }
線段樹初步(3)