一維線段並

2022-10-09 22:30:22 字數 1708 閱讀 1537

洛谷p1496 火燒赤壁

求數軸上n條線段的並。\(n \le 2 \times 10 ^ 4, - 2 ^ \le l, r \le 2 ^ \)

做法 \(:\) 基於暴力的做法:每條線段往桶裡加。

再加上兩個小技巧。

第乙個常用技巧是差分,即對於連續的中間無詢問的一些修改,可以用 \(\theta(1)\) 單點修改,全部搞完後再 \(\theta(n)\) 求字首和。以區間加為例, \(l,r\) 區間加 \(x\) ,就 \(c[l] += x\) , \(c[r + 1] -= x\) ,這樣求字首和就會正好加到這個區間上。

第二個是離散化。像本題這種資料很大,資料個數卻比較小的題,數軸上會非常地離散,中間一大坨都是空的,很浪費我們的青春。我們唾棄這種佔著茅坑不拉屎的行徑,所以智慧型的人類思考出了乙個簡單易行而又效果拔群的反抗方式:離散化。離散化就是說把陣列中的權值轉化為它的排名,這樣陣列內大小關係是不變的,資料範圍卻直接變成了 \(n\) ,於是掃一遍的複雜度就可行了。怎樣離散化呢?

scanf("%d", &n);

for(int i = 1; i <= n; ++i)

scanf("%d", &a[i]), b[i] = a[i];

sort(b + 1, b + n + 1);

m = unique(b + 1, b + n + 1) - b - 1; //要減一

for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b; //不減一

我們用乙個 \(b\) 陣列維護權值的大小關係,讓原來的 \(a\) 陣列維護排名。這樣的話如果到時候需要使用原值,就可以直接 \(b[a[i]]\) 呼叫,很方便。(好!) 解釋一下** \(:\) 先把 \(b\) 陣列排序,再去重( \(unique(it1,it2)\) 作用是將有序陣列 \([it1,it2)\) 去重,並返回去重後的陣列的最後乙個元素的下乙個位址。最後利用lower_bound()找到 \(a\) 陣列中對應的排名。

最後再說一下做題的細節。

m = unique(b + 1, b + (n << 1 | 1)) - b - 1; //離散化

for(int i = 1; i <= (n << 1); ++i)

for(int i = 1; i <= n; ++i)

++c[a[(i << 1) - 1]], --c[a[i << 1] + 1]; //差分

ans = m;

for(int i = 1; i < m; ++i)

cout << ans;

return 0;

}

二維線段樹

二維線段樹一般用樹套樹的方式實現,每個外層線段樹的節點對應一顆內層線段樹,整個線段樹存放在乙個二維陣列中。二維線段樹 poj2155 include include include include include include include include include include inc...

二維線段樹

二維線段樹矩陣區間查詢最大值 矩陣求和預處理後o 1 就能算出來,不用線段樹,除非有修改操作 先第一維在第二維,注意建樹有個順序問題,應該讓第一維度的先建完然後再建第二個維度 具體看 include include include using namespace std const int maxn...

二維線段樹

一維線段樹用來維護一維的空間,即乙個線段。二維線段樹用來維護二維的空間,即乙個矩形。二維線段樹的每個結點都是一棵一維線段樹,所以結構體陣列要開二維,再加上線段樹本身的性質,會占用很大記憶體,要儘量減少結構體內儲存的值的個數和長度,考慮到每個節點表示的線段的左右端點可以作為函式引數,所以不再儲存在結構...