這道題目是點分治的題目,與模板題不一樣的是,這是要統計小於路徑長度為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 ...