給你一棵樹,以及這棵樹上邊的距離,問有多少對點它們兩者間的距離小於等於kkk。
這道題是應該加強資料了。。。
當然如果這道題不打算做點分治模板題的話可以不用
o (n
nlogn
)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
logn)
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
logn)
o(\sqrt n\ \log \sqrt n)
o(n
logn)
的時間複雜度內求出距離乙個點有多少個點路徑長度≤
k\leq k
≤k。總時間複雜度o(n
nlogn
)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...