首先來看一道題:[heoi2013]segment
可以發現的是,實質上某個 \(x = k\) 處的最大值只有乙個,因此我們需要盡可能減少計算不優的線段。
那麼對於兩條線段 \(a, b(a \ne b)\) 它們左右端點橫座標相同,就只會產生如下四種情況:
從特殊情況出發,每次我們都插入一條 \([1, n]\) 的線段。
如果是前兩條情況,顯然不優的那條線段對全域性任意乙個位置的貢獻沒有更優的那條直線優,因此我們只需要儲存更優的那條直線即可。
如果是後面兩種情況,以 \(a.k > b.k\) 兩條線段交點在中點左邊為例。
顯然此時在右半區間 \(b\) 會被 \(a\) 完全覆蓋,\(b\) 不會對右邊區間的查詢有任何貢獻。
但 \(b\) 可能對左半邊區間的查詢有貢獻,因此我們將 \(b\) 傳入左半邊區間進行遞迴操作。
可以發現,每條線段只保留在了其可能對整個區間造成貢獻的最上層區間,因此每個查詢時最優的那些線段一定會被保留下來。
那麼查詢的時候就要一路將經過的所有節點上的最優線段取 \(\max\),類似於標記永久化。
你會發現每次我們修改最多會往半邊區間走,每次查詢同樣也是一樣,因此這個複雜度是 \(o(n \log n)\) 的。
回到原題,原題當中要求每次對乙個區間插入一條線段。
不難發現我們可以直接使用線段樹來維護這個東西,因為線段樹每次涉及修改的區間有 \(\log n\) 個,因此總複雜度是 \(o(n \log ^ 2 n)\) 的。
#include using namespace std;
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid (l + r >> 1)
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int n = 100000 + 5;
const int p = 39989;
const int pp = 1e9;
const double eps = 1e-6;
struct tree t[n << 2];
int n, k, x, y, xx, yy, ans, tot, opt;
int read()
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}double f(tree a, double x)
tree chkmax(tree a, tree b, int x)
void down(int p, int l, int r, tree k)
else
}}void update(int p, int l, int r, int x, int y, tree k)
if(mid >= x) update(ls, l, mid, x, y, k);
if(mid < y) update(rs, mid + 1, r, x, y, k);
}tree query(int p, int l, int r, int x, int y)
int main()
else );
else );}}
}return 0;
}
值得一提的是,這種涉及區間修改以及查詢(在某個值域區間範圍內也是一樣)的問題,往往可以先考慮所有操作都是整個區間的情況,而對區間進行操作往往可以使用線段樹多 \(1 \log\) 解決。 李超線段樹
t4正解李超線段樹?不會,滾過來學 貌似思路並不是很難的亞子 我們可以使用權值線段樹!對於每個區間,我們維護乙個最優線段 顯然對於乙個線段完全覆蓋的區間我們才處理 分四種情況討論 直接賦值 直接賦值 直接滾粗 最複雜的情況,我們考慮將覆蓋該區間最長的線段保留為最優線段 欸嘿?怎麼搞呢?其實只需判斷該...
李超線段樹
可以處理二維平面上加入線段,然後查詢單點最大值。首先我們定義乙個區間的最優勢線段,為區間中點值最大的線段,然後我們發現處理詢問,我們只需要將經過的線段樹節點上的最優勢線段對應的點值取max即可。然後考慮如何處理修改,首先將線段劃分到 o logn 個線段樹節點上。如果當前線段被最優勢線段完全覆蓋,那...
Segment 李超線段樹
要求在平面直角座標系下維護兩個操作 1.在平面上加入一條線段。記第 i 條被插入的線段的標號為 i 2.給定乙個數 k,詢問與直線 x k 相交的線段中,交點最靠上的線段的編號。若有多條線段符合要求,輸出編號最小的線段的編號 明顯的線段樹特徵 1.有固定的 區間長度,39989 2.插入元素支援合併...