cdq(時間分治)

2021-09-26 03:12:01 字數 2528 閱讀 5892

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 個二元組,每個...