洛谷P4178 Tree 分塊

2021-10-01 14:58:13 字數 3579 閱讀 9465

給你一棵樹,以及這棵樹上邊的距離,問有多少對點它們兩者間的距離小於等於kkk。

這道題是應該加強資料了。。。

當然如果這道題不打算做點分治模板題的話可以不用

o (n

nlog⁡n

)o(n\sqrt n\ \log \sqrt n)

o(nn

​logn​

)分塊在洛谷優秀的o2o2

o2下過了。。。

我們假設1

11為樹根,dfs

dfsdf

s遍歷一遍就可以得到每乙個節點x

xx到1

11的距離dis

[x

]dis[x]

dis[x]

。然後可以暴力判斷dis

disdi

s中有多少個數時小於等於k

kk的。

每乙個點作根跑一邊,時間複雜度o(n

2)

o(n^2)

o(n2)。

考慮優化上述演算法。

考慮換根。我們把根從1

11轉移到x

xx時,所有x

xx子樹內的節點到x

xx的距離減少了dis

(1,x

)dis(1,x)

dis(1,

x),其他點到x

xx距離增加了dis

(1,x

)dis(1,x)

dis(1,

x)。用dfs

dfsdf

s序把每一棵子樹的編號變為連續的,那麼我們只要在原dis

disdi

s序列中進行區間加減操作就可以了維護出以x

xx為根時,每乙個點到x

xx的距離。

那麼我們現在需要求整個dis

disdi

s陣列中有多少個是≤

k\leq k

≤k的,那麼其實就是 教主的魔法 那道題了。

採用分塊,區間加減容易實現,對於每乙個塊維護乙個sor

tsort

sort

陣列,表示這個塊的dis

disdi

s排序後的陣列。那麼每次修改時,sor

tsort

sort

陣列可以在o(n

log⁡n)

o(\sqrt n\ \log \sqrt n)

o(n​

logn​)

的時間複雜度內暴力維護。

注意如果要把乙個塊整體加減,那麼直接在這個塊的add

addad

d陣列中加減即可,在dis

disdi

s和sort

sort

sort

中都不需要修改。

詢問時列舉每乙個塊p

pp,在這個塊的sor

tsort

sort

中二分出第乙個嚴格大於k−a

dd[p

]k-add[p]

k−add[

p]的數的位置pos

pospo

s,那麼這個塊中小於等於k

kk的數字就有pos

−1

pos-1

pos−1個。

這樣就可以在o(n

log⁡n)

o(\sqrt n\ \log \sqrt n)

o(n​

logn​)

的時間複雜度內求出距離乙個點有多少個點路徑長度≤

k\leq k

≤k。總時間複雜度o(n

nlog⁡n

)o(n\sqrt n\ \log \sqrt n)

o(nn

​logn​

)。常數極大。

#include

#include

#include

#include

using

namespace std;

const

int n=

40010

,m=210

;int l[m]

,r[m]

,pos[n]

,add[m]

,sort[m]

[m],head[n]

,dis[n]

,dis[n]

,rk[n]

,dfn[n]

,size[n]

;int n,m,t,tot,ans;

struct edge

e[n*2]

;void

addedge

(int from,

int to,

int d)

void

dfs1

(int x,

int fa)

//求出每乙個點到點1的距離,同時求出dfs序}}

void

update

(int l,

int r,

int val)

//分塊區間加模板

for(

register

int i=l;i<=r[p]

;i++

) dis[i]

+=val;

for(

register

int i=l[q]

;i<=r;i++

) dis[i]

+=val;

for(

register

int i=l[p]

;i<=r[p]

;i++

) sort[p]

[i-l[p]+1

]=dis[i]

;for

(register

int i=l[q]

;i<=r[q]

;i++

) sort[q]

[i-l[q]+1

]=dis[i]

;//兩邊暴力修

sort

(sort[p]+1

,sort[p]+1

+r[p]

-l[p]+1

);sort

(sort[q]+1

,sort[q]+1

+r[q]

-l[q]+1

);for(

register

int i=p+

1;i) add[i]

+=val;

}void

dfs2

(int x,

int fa)

//換根求答案}}

intmain()

scanf

("%d"

,&m)

; tot=

0; t=

sqrt

(n);

if(t*t;dfs1(1

,0);

for(

register

int i=

1;i<=t;i++

)dfs2(1

,0);

printf

("%d"

,(ans-n)/2

);//先減去n個(x,x)的點對,然後(x,y)和(y,x)只算乙個,所以除以2

return0;

}

洛谷P4178 Tree(點分治)

考慮點分治,對於乙個子樹,求出每個點到根的距離並排序,用雙指標掃瞄,但這樣會把來自同乙個兒子的路徑也統計上去,再對每個兒子做一遍,減去即可。include include include include using namespace std typedef long long ll const i...

洛谷4178 BZOJ1468 Tree題解點分治

點分治的入門練習。題目鏈結 bzoj的鏈結 許可權題 關於點分治的思想我就不再重複了,這裡重點說一下如何判重。我們來看上圖,假設我們去除了1節點,求出d 2 1,d 3 d 4 2 假設k為5,這樣我們會認為節點 2,3 2,4 3,4 的距離小於k,從而累計到答案中 但是我們以2為root做點分治...

洛谷P1816 忠誠 分塊

分塊真的是很暴力呀 暴力查詢左端,暴力查詢又端點,中間整體部分直接 o 1 求出。注意程式設計細節 belong i i 1 block 1 這樣可以保證序列被分成這樣的 code include include include includeusing namespace std const in...