官方題解其實講的挺清楚了,就是鍋有點多……
一些有啟發性的部分分
L=N
一個經典(反正我是不會)的容斥:最后的答案=對於每個點能夠以它作為集合點的方案數-對於每條邊能夠以其兩個端點作為集合點的方案數。原因是:對於每一種合法方案,集合點一定是樹上的一個連通塊,滿足\(n=m+1\)。算點時,這種方案被算了\(n\)次;算邊時,這種方案被算了\(m=n-1\)次,所以每一個方案都恰好被算了一次。
有\(DP\):設\(f_i-1\)表示選擇了包含\(i\)和\(i\)的子樹中的點的一個連通塊的方案數,轉移枚舉每一個兒子選不選。然后設\(g_i\)表示以\(i\)為原樹的根,選擇了包含\(i\)和\(i\)的子樹中的點的一個連通塊的方案數,這個在求完\(f\)之后換根DP,換根的時候可以同時計算出每條邊的方案。
NL較小
改一下\(N=L\)的DP:設\(f_{i,j}-1\)表示選擇了包含\(i\)和\(i\)的子樹中的點的一個連通塊,其中距離\(i\)點最遠的點與\(i\)距離\(\leq j\)的方案數,\(g_{i,j}\)表示以\(i\)為原樹的根,選擇了包含\(i\)和\(i\)的子樹中的點的一個連通塊,其中距離\(i\)點最遠的點與\(i\)距離為\(\leq j\)的方案數。
同樣可以求完\(f\)后換根DP求\(g\),但是有個問題:如何從\(f_{fa_x}\)中去掉\(f_x\)的貢獻。可以在計算\(f\)的時候計算每一個點合並所有孩子的一個前綴的答案、合並所有孩子的一個后綴的答案,這樣把一個前綴和一個后綴拼起來就可以得到去掉\(f_x\)的貢獻的\(f_{fa_x}\)。
鏈
枚舉一下點和邊,方案數可以直接算
k=1
可以枚舉選擇的連通塊的根,用長鏈剖分+線段樹優化,與正解關系不大所以略過
正解
從\(NL \leq 10^7\)開始。稍微修改一下\(g_{i,j}\)的定義:設\(g_{i,j}\)表示選擇\(i\)、不選擇\(i\)的子樹,選擇的連通塊中最大距離\(\leq j\)的方案數。
f的優化
注意到\(f\)可以長鏈剖分優化。在長鏈剖分的轉移中我們需要支持后綴乘(因為后綴有一段要乘的值相同)、整體加(轉移完成之后所有\(f_{i,j}\)需要\(+1\)),可以給長鏈剖分打乘法標記\(a\)和加法標記\(b\)解決,一次后綴乘就把前面沒有乘的部分乘上逆元。可能會有乘\(0\)的問題,所以還需要一個后綴賦值標記,當乘\(0\)時把當前的后綴賦值為\(-\frac{b}{a}\)。
g的優化
\(g\)是一個換根DP,但是也可以用長鏈剖分優化。將重鏈和輕邊的轉移分開考慮:①對於重鏈直接暴力將輕兒子的\(f\)值轉移過來,復雜度跟長鏈剖分一樣;②對於輕邊,假設這條輕邊轉移到的點\(x\)的子樹深度為\(p\),那么因為在最后統計答案的時候只需要每個點的\(g_{x,L}\),所以\(x\)子樹中只有\(g_{x,j}(j \in [L - p , L])\)有用,可以只轉移這一些值。那么每一條輕邊就只會轉移輕子樹深度個\(g\),總復雜度也是均攤\(O(n)\)的
g的轉移時的一個問題
我們還需要解決在轉移\(g\)的過程中要取孩子的一個前綴和一個后綴的問題。可以主席樹但是\(O(nlogn)\)有點慢。考慮按照求\(f\)時DFS順序的反序DFS求\(g\),我們到達一個點、即將遞歸到下一個點的時候,將這個點對於當前點的\(f\)的貢獻消除,這樣可以得到前綴的值,再拿另一個數組維護一下后綴的\(f\)值就可以得到前綴和后綴。支持撤銷只需要在每一次從輕邊修改一個點時把所有標記和將要修改的位置的值記錄下來打包丟到一個棧里面。
線性求逆元
有一個預處理逆元的科技:可知要求逆元的數一定是\(f_{x , p}\),其中\(p\)是\(x\)的子樹大小,所以用\(L=N\)部分分的方法將所有\(dp_{x,p}\)預處理出來,然后用類似於預處理階乘逆元的方式預處理逆元,在更新乘法標記\(a\)的同時同步更新\(a^{-1}\),就可以做到嚴格的\(O(n)\)求解。
細節非常非常的多……寫題兩小時調試一整天