本題是要你任選乙個點為根使得樹上的所有點深度之和最小。
本題是要你任選乙個點為根使得樹上的所有點深度之和最小。
首先考慮如果是指定根節點你會不會做:已知根節點的話,我們只需要一遍dfs(或者bfs)就可以求出每個點的深度,然後求和就可以了。
然後我們考慮如果我們知道當前節點x為根的結果能否快速求出以它某兒子y為根的結果——當y從根的兒子變成根,它和它的子樹都向上提了一層,深度-1,而x往下了一層,它除了y以外的其他子樹也一起往下了一層,深度加以。所以從以x為根變為以y為根,深度之和減少了y的子樹的大小,增加了其他點數的個數。
如果我們用f[i]表示以i為根的時候每個點深度的和,我們可以用一遍dfs把f[1]求出來——以1為根dfs,給每個點維護乙個dep[i],顯然dep[y] = dep[x]+1(y是x的兒子),dfs結束之後求和就好。根據之前的分析在這次dfs的時候我們還需要同時維護乙個陣列cnt[i]表示i的子樹大小(包括它自己),cnt[x]=∑cnt[y](y是x的兒子)。然後再來一次dfs,遍歷到x點的時候,去計算x的每個兒子y的f值,f[y]= f[x] -cnt[y] + (n- cnt[y])
最後只需要取f[i]的最小值就是答案。
這種解法實際上是利用了換根的思路,即先算出固定某一點為根的答案然後考慮把它的兒子換成根會發生什麼樣的變化,如果這個變化是比較好算的,那麼我們就可考慮每個點x為根的答案都根據以它父親為根的結果去推。
#include
#include
#include
#include
#include
#define debug printf("i am here\n");
using
namespace std;
typedef
long
long ll;
const
int maxn=
1e6+5;
const ll inf=
0x3f3f3f3f3f3f3f3f
;int n,cnt,head[maxn]
,dep[maxn]
,sz[maxn]
;ll ans[maxn]
,mindep=inf;
//ans[i],以i為根的ans
struct nodee[maxn<<1]
;void
add(
int u,
int v)
void
dfs1
(int son,
int fa)
ans[1]
+=dep[son];}
void
dfs2
(int son,
int fa)
mindep=
min(mindep,ans[son]);
}int
main()
dfs1(1
,1);
dfs2(1
,1);
printf
("%lld\n"
,mindep)
;return0;
}
首先,我們來看是不是能求出以某固定點(以1為例)的流量:
我們用flow[i]表示i點的流量:
flow[i]=∑min(flow[j],edge[i][j])(j是i的兒子)
這裡特別要注意i點是葉子節點的情況,我們可以認為它的flow就是連向它的邊流量就可以了。
接著,我們來考慮換根,flow[i]表示以i為根的流量——考慮當根從x變成x的兒子y的時候會發生什麼?哪些量邊哪些量不變? (想不清楚就畫圖,做演算法題「草稿紙」還是很重要的。)
x的流量中沒有從y過來的部分了 ,即此時x的流量其實是flow[x] - min(flow[y], edge[x] [y])但是此時的flow我們用它來表示以i為根的流量了(其實你也可以用另外乙個陣列存,但是並沒有必要),所以並不更新flow[x]。
而x的流量會流向y,注意這裡是x的新流量,而不是flow[x]。flow[y] = flow[y] + min(flow[x] - min(flow[y], edge[x] [y]), edge[x] [y])
特別注意葉子結點,當y是葉子節點的時候沒關係,但是當x從根變為了葉子節點(說明x除了y這個兒子沒有別的兒子了),它的流量並不是變成的上面的式子,而是直接變成edge[x] [y]。
#include
#include
#include
#include
#include
#define debug printf("i am here\n");
using
namespace std;
typedef
long
long ll;
const
int maxn=
2e5+
5,inf=
0x3f3f3f3f
;const ll inf=
0x3f3f3f3f3f3f3f3f
;int t,n,cnt,head[maxn]
,flow[maxn]
,deg[maxn]
,ma;
struct nodee[maxn<<1]
;void
add(
int u,
int v,
int w)
void
dfs1
(int son,
int fa)
dfs1
(e[i]
.to,son)
; flow[son]+=
min(flow[e[i]
.to]
,e[i]
.w);}}
void
dfs2
(int son,
int fa)
}void
init()
}int
main()
dfs1(1
,1);
dfs2(1
,1);
for(
int i=
1;i<=n;i++
)printf
("%d\n"
,ma);}
return0;
}
樹形 dp 換根 dp
樹形dp 樹形動歸一般是依賴於dfs的,根據動歸的後效性,父節點的狀態一般都依賴子節點的狀態以某種方式轉移而來 換根的p2015 設f i j 表示i的子樹上保留j條邊最多蘋果數 p2279 狀態表示f x 0 覆蓋到x的爺爺和x整棵子樹 向上2層 最少個數 f x 1 覆蓋到x的父親和x子樹 向上...
樹形DP 換根DP
某些樹形dp問題中,我們要求的值是類似 以當前節點為根節點得到的答案 卻沒有給出固定的根節點,若仍然按照常規的樹形dp思路對每個點進行dp,我們對每乙個節點均進行一次 dfs 最後的複雜度是 o left n 2 right 如果我們先假設任意乙個點為根進行 dp,求出當前樹形結構下以每個點為根的子...
樹形dp之換根dp
樹形dp之換根dp 換根dp是樹形dp這一類中我覺得比較難的一類。一般的樹形dp都只需要從子樹往父親推,然而換根dp則需要從父親往子樹推,接下來寫寫我學習換根dp的幾個例題。例題1 computer 題目大意 給你一棵樹,然後問你每乙個點具體其他點最遠的距離是多少。解題 這個題目首先任意找乙個點為根...