洛谷p4178 點分治 線段樹

2021-09-28 15:27:21 字數 3975 閱讀 9245

這道題目是點分治的題目,與模板題不一樣的是,這是要統計小於路徑長度為k的點對數。我們分析下與模板題的區別,模板要求的是路徑等於k的長度的路徑是否存在,然後統計的方法是對於每個重心(跟),求出各點與根的路徑,然後重心下面的各個子樹中,相同的子樹不去統計答案,去計算之前非該子樹的路徑能否湊出k的路徑,也就是吧路徑分成了(當前子樹的路徑與非當前子樹的路徑),然後其實我們也可以利用這個模板題的思想,一旦當前子樹的路徑與非當前子樹的路徑能湊出小於k的路徑,那麼這就是乙個就是符合條件的點對,我們可以對於每種路徑的數目去記錄一下,就是記錄有多少條路徑可以達到這個路徑數,但是答案就是加上這個長度路徑的點數。

for

(register

int x =

1; x <= cnt; x++

)

temp儲存的就是當前子樹的路徑的長度,然後judge[i]就是儲存的存在i這條路徑的點的數目。我們去列舉[1,k](**是有個小優化,因為一些長度是非法的無法更新答案)所有的可能路徑,然後去加起來。

這樣就足夠了嗎?並不是,為什麼?因為我們算答案只是算了當前子樹與非當前子樹的路徑能不能湊,當前子樹的路徑我們沒去統計,所以其實就是只要在最後,我們把所有judge陣列裡面[1-k]的點數都加到ans上面去就可以了,因為judge存的就是當前子樹的所有路徑,然後當前子樹只要存在小於k的也就是乙個點對。

while

(!que.

empty()

)

好了如果這樣子,他普通的就只有50分,(inline,register,快讀亂飛可以達到7,80),因為超時;為什麼?我們來看這個統計答案的**

for

(register

int x =

1; x <= cnt; x++

)

這個統計答案是乙個平方級別的統計答案,所以爆掉了,怎麼優化?我們思考一下,這第二個for算的其實就是

judge[1]+judge[2] + judge[3]+…+judge[m-temp[x]];

這其實就是乙個字首和對不對,然後我們去每次更新judge陣列的時候都是單點更新(找到一條路徑, 在路徑長度的judge下面++);這裡就可以用樹狀陣列或者線段樹去優化得答案了,就是乙個簡單的單點更新區間查詢。好了這題就做好了。

(用樹狀陣列wa了4個小資料我也不知道為什麼,一改線段樹就a)

看起來**很長~~(線段樹太長了!)~~ ,其實都是模板套上去然後稍稍改進下(比如judge陣列從記錄邊是否存在變成了存在這條邊的數目)

#include

#include

#include

#include

#include

using

namespace std;

const

long

long max_ =

100000+7

;const

int inf =

1e9+7;

inline

intread()

while

(ch >=

'0'&&ch <=

'9')

return s * f;

}int head[max_]

, xiann =

2, n, m, sum, root, vis[max_]

, size_[max_]

, maxpart[max_]

, judge[max_]

, temp[max_]

, dis[max_]

, cnt =

0, ask[max_]

, ans =0;

struct k xian[max_ *2]

;inline

void

add_

(int a,

int b,

int c)

inline

void

getroot

(int now,

int fa)

maxpart[now]

=max

(maxpart[now]

, sum - size_[now]);

if(maxpart[root]

> maxpart[now]

)root = now;

}inline

void

getdis

(int now,

int fa)

}struct kk tree_[max_ *4]

;void

build

(int l,

int r,

int node)

int mid = l +

(r - l)/2

, l_tree = node *

2, r_tree = node *2+

1;build

(l, mid, l_tree)

;build

(mid +

1, r, r_tree)

; tree_[node]

.sum = tree_[l_tree]

.sum + tree_[r_tree]

.sum;

}void

pushdown_

(int node,

int vv)

void

update

(int l,

int r,

int node,

int aiml,

int aimr,

int vv)

int mid = l +

(r - l)/2

, l_tree = node *

2, r_tree = node *2+

1;update

(l, mid, l_tree, aiml, aimr, vv)

;update

(mid +

1, r, r_tree, aiml, aimr, vv)

; tree_[node]

.sum = tree_[l_tree]

.sum + tree_[r_tree]

.sum;

}int

askk

(int l,

int r,

int node,

int aiml,

int aimr)

int mid = l +

(r - l)/2

, l_tree = node *

2, r_tree = node *2+

1;int t1 =

askk

(l, mid, l_tree, aiml, aimr)

, t2 =

askk

(mid +

1, r, r_tree, aiml, aimr)

;return t1 + t2;

}inline

void

solve

(int now)

for(

register

int x =

1; x <= cnt; x++)}

while

(!que.

empty()

)}inline

void

divide

(int now)

}int

main()

m =read()

;build(1

, m,1)

; maxpart[0]

= sum = n;

root =0;

getroot(1

,0);

getroot

(root,0)

;divide

(root)

;printf

("%d"

, ans)

;return0;

}

洛谷P4178 Tree(點分治)

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

洛谷 P3806 模板 點分治1 點分治

傳送門 點分治!點分治.滾,雖然也沒有可以講的扣就完事了理解好就行 這裡講一下自己計算答案時的乙個部分,我們設tti tt i tti 表示到當前重心的距離為i ii的路徑是否存在 對於每個詢問q iq i qi 因為我們便利完前面的i 1 i 1i 1棵子樹,所以用ttq i itt ttqi i...

洛谷P3806 模板 點分治1 點分治

感謝hzwer的點分治互測。給定一棵有n個點的樹 詢問樹上距離為k的點對是否存在。輸入格式 n,m 接下來n 1條邊a,b,c描述a到b有一條長度為c的路徑 接下來m行每行詢問乙個k 輸出格式 對於每個k每行輸出乙個答案,存在輸出 aye 否則輸出 nay 不包含引號 輸入樣例 1 複製2 1 1 ...