一般如果是從左往右掃的話,首先用結構體表示圖形(長方形或正方形)的左右兩邊;
struct ddu[n*2]
;
f 表示該邊時入邊還是出邊,入邊為 1 , 出邊表示 -1 ;
x 表示該邊的x座標,y1 表示該邊的上邊y座標,y2 表示該邊的下邊y座標;
如圖:這個還沒完,可以發現y的取值範圍為1-1e9,因為我們線段樹維護的是y(這個後面講),所以肯定爆空間,圖形數量是1e5,所以離散化y是必須的一步;
y離散化完了,就再把結構體du以x由小到大排序,然後從左往右掃;
再說線段樹維護什麼;
struct nodetr[n*8]
;
l,r意義不變,len表示的就是該區間的長度,lz表示該區間被加入的次數(也就是上面的入邊和出邊 f );
最最最重要也最難的部分來了:
voi***(
int k)
記住,掃瞄線區間修改操作不需要往下轉標記,也就說不需要每個葉子結點的值都改變;
所以只要pp,向上傳就可以;
當lz有值時,就是說該區間有邊加入時,那麼該區間的長度len直接等於r-l(注意l,r以被離散化,val表示離散化前的原值),這裡還有個r+1,也是非常非常重要難理解的一部分,之後再講;
當lz沒有值時,只要加上左右區間的長度即可;
我們查詢的時候,一般是直接查全域性的長度,tr[1].len;
所以最後我們只要區間修改,和直接查詢即可;
ll ans=0;
for(
int i=
1;i)
修改的值就是du[i].f,表示該邊是加入還是出去,這裡為啥是y2-1,這跟前面為啥是r+1有聯絡;
現在講前面最重要,最難理解的黃色部分:一切的一切都是離散化導致的;
假設,l=1,r=m,我們要在區間[1,m]中加1,是不是直接len=len[d]-len[l]+len[r]-len[d+1];
這樣算出來的len是錯的,為啥?因為d–d+1之間的距離我們沒算,這也是線段樹維護的y的距離而不是傳統的線段樹的區別了;
全部**來自這道題:
洛谷·p5490 【模板】掃瞄線
**:
#include
#define ll unsigned long long
#define pa pair
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using
namespace std;
const
int n=
100010
;const
int m=
50100
;const ll mod=
10007
;int n,x_1[n]
,x_2[n]
,y_1[n]
,y_2[n]
,a[n*2]
,cnt,tot,val[n*2]
;struct ddu[n*2]
;struct nodetr[n*8]
;void
build
(int l,
int r,
int k)
voi***(
int k)
void
update
(int l,
int r,
int w,
int k)
int d=
(tr[k]
.l+tr[k]
.r)>>1;
if(l<=d)
update
(l,r,w,ls);if
(r>d)
update
(l,r,w,rs);pp
(k);
}bool
cmp(d p,d q)
intmain()
sort
(a+1
,a+cnt+1)
; cnt=
unique
(a+1
,a+cnt+1)
-a-1
;for
(int i=
1;i<=n;i++
)sort
(du+
1,du+tot+
1,cmp)
;build(1
,cnt-1,
1); ll ans=0;
for(
int i=
1;i)printf
("%lld\n"
,ans)
;return0;
}
掃瞄線演算法
給出幾個矩形對角端點座標,求這些矩形整體覆蓋的面積。基本思想如下圖 先離散化。掃瞄線 是一根想象中的虛線,從左往右掃瞄,遇到 矩形 則成為 事件 遇到 起始邊 則update相應區間的 厚度 或者 覆蓋次數 covercnt 1。遇到 結束邊 則update相應區間的 厚度 covercnt 1。用...
X 掃瞄線演算法
多邊形有兩種重要的表示方法 頂點表示和點陣表示 頂點表示是用多邊形的頂點序列來表示多邊形。這種表示直觀 幾何意義強 佔記憶體少,易於進行幾何變換。但由於它沒有明確指出哪些象素在多邊形內,故不能直接用於面著色 點陣表示是用位於多邊形內的象素集合來刻畫多邊形。這種表示丟失了許多幾何資訊 如邊界 頂點等 ...
區域填充之掃瞄線演算法
區域的填充可以根據區域的填充,採用不同的填充演算法,而其中有掃瞄線類演算法和種子填充演算法。這裡,先介紹掃瞄線類演算法之有序邊表的掃瞄線演算法。其他什麼種子填充 邊界標誌演算法 4連通區域的遞迴演算法 8連通區域的遞迴演算法 掃瞄線種子填充演算法比較簡單。其實有序邊表其實領會了也好理解,關鍵是將思想...