explorer
題意:給定n個點m條無向邊,每條邊有乙個寬度(l,r)。現在有乙個人在一號節點,這個人的寬度不知,問最後這個人 到達n號節點可以有多少種大小。通過一條邊的條件是這個人的寬度大小在這條邊的範圍之內。(用詞不準確,但大概題意就是這樣tttt。。。)。
菜鳥一看見這道題,這不dfs搜尋一下就ok了,菜鳥還是頭腦太簡單了。
看了官方題解,時間分治????這是什鬼東西呢,表示不知道。後來才知道就是cdq分治,以前聽說過cdq,但不知道還有乙個名字時間分治。
前置技能:並查集按秩合併
並查集集合並的時候可以通過路徑壓縮的方式去提高查詢的效率,缺點就是如果想撤銷操作的話就比較困難了。因此有了另外一種合併方式——按秩合併。這裡的秩指的是樹的高度,有的地方也說是集合的大小。
每次把秩小的合併到秩大的。
為什麼要這樣呢?我們沒有路勁壓縮,但希望可以提高查詢的效率,因此需要盡量維護這顆樹的平衡
h[b]
=max
(h[a]+1
, h[b]
);
h:表示節點秩的大小。並且b的秩大於等於b的秩。如果b的秩嚴格大於a的秩,那麼合併之後的秩不變。如果a,b的秩相同。那麼就需要加一。想不明白的自己畫個圖看一看就很清楚了。
此題思路:
這裡的寬度區間比較大,所以需要先離散化一下。對於一條可以從1到n的路徑,其實我們並不關心這條路徑具體走過那些節點。我們只需要知道它可以到達n,以及這條路勁上的區間並。假設對於選擇了k條邊,那麼把每條邊對應的兩個點用並查集合並到一起,如果最後1和n在同乙個集合裡。那是不是就說明這條路勁可行呢?顯然是的。
具體怎麼做呢。建立一顆線段樹,每個節點表示這個區間內的邊的節點。假設一條邊是(u,v,l,r)那麼就需要在區間[l,r]上加入a,b這兩個點,因為對於所有[l,r]的子區間,他們都是可以走a,b這條邊的。把所有邊的資訊加入線段樹之後。就可以開始查詢了。
從根節點開始往下尋找。每經過乙個節點就把這個區間內的點用並查集合並。最後走到葉子節點的時候判斷一下1和n是否聯通,如果聯通就累加上該區間對答案的貢獻。最後回溯的時候需要撤銷並查集的操作,因此合併並查集的時候記錄一下合併的點的資訊,方便後來撤銷操作。
在區間離散化的時候,右區間r+1,可以防止左右端點相同,區間退化為點的情況。最後的右區間對應的離散化後的小標需要減1.
#include
using namespace std;
typedef pair<
int,
int>pii;
const
int n =
100*
1000+10
;int n, m, sz;
int h[n *2]
, f[n *2]
, a[n *2]
;int ans;
struct node edge[n]
;void
init()
}int
find
(int x)
intgetid
(int x)
vectorsum[n <<3]
;void
add(
int rt,
int l,
int r,
int l,
int r,
int a,
int b));
return;}
int mid = l + r >>1;
if(l <= mid)
add(rt <<
1, l, mid, l, r, a, b);if
(r > mid)
add(rt <<1|
1, mid +
1, r, l, r, a, b);}
void
ask(
int rt,
int l,
int r));
f[a]
= b;
h[b]
=max
(h[a]+1
, h[b]);
}if(l == r) ans +=(
find(1
)==find
(n)?a[l+1]
-a[l]:0
);else
for(
int i =
0; isize()
; i++)}
intmain()
sort
(a +
1, a +1+
2* m)
; sz =
unique
(a +
1, a +1+
2* m)
- a -1;
for(
int i =
1; i <= m; i++
)ask(1
,1, sz)
;printf
("%d\n"
, ans)
;return0;
}
CDQ分治概述
log l og 的時間把它變成離線問題。正好有些題目的離線問題是比較簡單的。具體是什麼意思呢?我們對於每一層分治,只考慮前一半對於後一半的影響,然後在每個詢問當中記錄下來影響。最後把所有影響合併就可以得到每乙個詢問的答案。舉個例子 區間修改區間查詢。首先,在時間軸上離線分治。每一層分治後把詢問和查...
CDQ分治總結
cdq這個東西嘛,說容易其實也很容易,說難其實也有些難,但只要細細品味,定能發現其中的真理的!那真理,也會像蝴蝶一般,破蛹而出,化身為一道亮麗的風景線。題記。咳咳,閒話就講到這裡了,切入正題。首先我們來了解一下cdq分治這個東東。cdq分治,他的常數小,但必須離線操作the most importa...
cdq分治小結
一般的分治,眾所周知的,是通過將大的問題拆小,然後對小問題的答案進行合併得到大問題的答案,但是cdq分治不是。我們知道,分治時,將乙個區間從中間斬開,分兩半處理,cdq分治在處理完之後,不是合併答案,而是計算左區間對右區間的貢獻,這樣子可以將維度降低,問題就更好做了。現在有 n nn 個二元組,每個...