這道題又是一道換根dp板子題,**結構與 accumulation degree 這道題基本一致,唯一不同的就是轉移了【不過轉移的時候,因為方程的原因不需要特殊考慮葉節點】
我們先套路的設\(dp[i]\)表示以\(i\)為根的子樹中,所有點的深度和,現在,我們來想想轉移。
我們發現,如果我們要從\(i\)的乙個兒子v轉移到i的話,以v為根的子樹中的所有節點的深度都加了1,現在我們就不能夠直接求值了,怎麼辦呢?很簡單,我們發現,既然以v為根的子樹中的所有節點的深度都加了1,那麼,一共增加的值,不就正好是以v為根的子樹中節點的個數嘛?
所以,我們只需要再維護乙個以i為根的子樹的大小\(siz[i]\)即可
那麼,我們就可以很簡單的推出轉移:
\(dp[i]=\)
\(\sum_(dp[v]+siz[v])\)
\(siz[i]=\)
\(1(i本身)+\sum_siz[v]\)
這樣,我們一遍dfs就可以求出所有\(dp[i]\)了!
現在,我們考慮換根dp
我們假設將根\(x\)換成了它的乙個兒子\(y\),那麼,同樣的,改變的只是\(x\)和\(y\)的子樹,我們將這個"影響"修改一下即可~
首先,\(dp[i]-=(dp[v]+siz[v])\)(減去v的貢獻)
然後,\(siz[i]-=siz[v],siz[v]+=siz[i]\)(修改兩個點的子樹大小)
最後,\(dp[v]+=(dp[i]+siz[i])\)(加上i的貢獻)
為什麼是這個順序呢?因為修改\(dp[i]\)的時候,\(dp[v]\)和\(siz[v]\)必須和以前一樣(因為我們是從這兩個值轉移過來的),然後要修改\(dp[v]\)的話,\(dp[i]\)和\(siz[i]\)又必須是修改後,沒加v的貢獻的值,所以,這樣安排修改順序是很好的。(當然,你非要用其他順序的話,我也無話可說,只是要注意修改的方法有可能會不同)
最後,我們每次統計下以任意點為根時,\(dp[i]\)的值的最小值即可~
**:
#include#define int long long
using namespace std;
const int n=1e6+1;
struct nodet[n<<1];
int dp[n],siz[n];
int las[n],len,ans=1e17;
inline void add(int u,int v),las[u]=len;
}inline void dfs1(int now,int fa)
}}inline void dfs2(int now,int fa)
}}signed main(){
int n;
scanf("%lld",&n);
for(int i=1;i 其實一開始,我變數都開的是int,然而,交上去後,算了下,每次的貢獻最大可以達到\(n^2\)水平【鏈】,於是發現很可能爆int,於是,我馬上改成long long,然後,改完後正準備提交。。。
恭喜通過~
我:???
NC16649 校門外的樹 NC24636 值周
題目鏈結 校門外的樹 值周 資料加強版,加到了1e8 題意 從 0 l 中選取m個區間 l r 刪除 l r 中的元素,計算最後剩餘多少個元素 思路 字首和 差分標記端點 o n includeusing namespace std int a int 1e8 5 int l,r int main ...
NC13249 黑白樹 樹上貪心
一棵n個點的有根樹,1號點為根,相鄰的兩個節點之間的距離為1。樹上每個節點i對應乙個值k i 每個點都有乙個顏色,初始的時候所有點都是白色的。你需要通過一系列操作使得最終每個點變成黑色。每次操作需要選擇乙個節點i,i必須是白色的,然後i到根的鏈上 包括節點i與根 所有與節點i距離小於k i 的點都會...
NC19775 平衡二叉樹 貪心
這題的第一想法就是我們對根節點進行貪心構造,使得左子樹和右子樹的差最大,這是最優的 左子樹自然是以n 1為高度的滿二叉樹,這樣節點最多,那麼右子樹我們需要考慮如何滿足高度的平衡限制 我們發現為了對每個點都滿足左右子樹差值為d,我們相當於對於每個節點,左子樹是乙個高度為i 1的最少節點的樹,而右子樹是...