HDU Cotree 換根dp,樹的重心

2021-09-29 14:58:55 字數 2780 閱讀 2584

我是看了這位大佬的部落格,感謝大佬他的部落格

題目描述:有兩顆樹,需要在這兩棵樹之間新增一條邊,這樣就變成了一棵樹,求這棵樹任意兩點之間的最小距離和

即$$\sum ^\sum ^dis\left( i,j\right)$$

那麼怎樣找重心呢?我們定義sum[i]:一顆樹中其他點到i點的距離和 sum_num[i]:為i的子節點數。任意選取乙個點為根,這兩個陣列可以用跑一次dfs求出來,找重心就是就是找到乙個合適的根,使得sum[根]最小,我們可以利用換根來實現。假設u是根,v是u的緊鄰子節點,我們可以通過依次執行下面的四個步驟實現將根換為v,你可以將圖畫出來,這樣容易理解

1.sum[u]-=(sum[v]+sum_num[v]+1)其中sum_num[v]+1是因為v的子節點及本身到u都還得加上乙個距離,v的子節點本來是到v的,現在到u,每乙個子節點到u得加上u->v這條邊

2.sum_num[u]-=(sum_num[v]+1),將u的子節點數減去v的子節點數及其本身

3.sum[v]+=(sum[u]+sum_num[u]+1)理解和第乙個步驟一樣

4.sum_num[v]+=(sum_num[u]+1),理解和第二個步驟一樣

用dfs將所有可能為根的點跑一遍,用root1,root2,sum_root1,sum_root2記錄找到的重心和其他點到重心的距離和

找到兩顆樹的重心實現連線之後,我們就可以求任意兩點之間的距離和了。求之前tree1任意兩點間的距離和可以在換根的同時將每乙個點作為根之後的sum[根]求和除以2就可以了,同樣可以求得tree2中任意兩點之間的距離和。然後再加上任意兩點(乙個在tree1中,乙個在tree2中)距離和。tree1中的任意一點想要和tree2中的一點連線,勢必要通過新加入的那條邊,(sum[root1]+sum_num[root1]+1)(sum_num[root2]+1),tree2中的一點與tree1中的所有點的距離和為sum[root1]+sum_num[root1]+1(這個距離是部分距離,起點是tree1中的任意一點,終點至root2,sum_num[root1]+1是tree1的節點數),因為tree2中有(sum_num[root2]+1)個點,所以要乘以(sum_num[root2]+1)。最後還要加上sum[root2](sum_num[root1]+1),這個是因為tree1中的任意乙個節點,路經root2和tree2中的所有節點距離的求和,而tree1中有sum_num[root1]+1個點,所以要乘以sum_num[root1]+1

因此這道題的答案就是

$$jie=tota/2+totb/2+(sum[roota]+sum_num[roota]+1)*(sum_num[rootb]+1)+(sum_num[roota]+1)*sum[rootb]$$

**

#pragma gcc optimize(2)

#include

using namespace std;

#define pi acos(-1.0)

#define e exp(1.0)

typedef

long

long ll;

const ll maxn=

1e5+7;

ll n,cnt,sum[maxn]

,sum_num[maxn]

,head[maxn]

;bool vis[maxn]

;struct edge//鏈式前向星存圖

e[maxn*2]

;//乘上2 是因為是無向邊,每條邊存兩次

void

add(ll u,ll v)

void

dfs(ll now)

//求出sum[i]以及sum_num[i]

} sum[now]

=temp_sum;

sum_num[now]

=temp_sum_num;

return;}

ll change_root

(ll now,ll &root,ll &root_sum)

//換根

tot+

=change_root

(v,root,root_sum)

;//為了計算各個兩點之間的距離和

sum[now]

=temp1;

sum_num[now]

=temp2;}}

return tot;

}int

main()

ll root_a,root_b,roota_sum,rootb_sum;

//樹的重心 以及其他點到重心的距離和

dfs(1)

; root_a=1;

//初始化

roota_sum=sum[1]

;for

(i=1

;i<=n;i++

)//找到另一顆樹

}memset

(vis,0,

sizeof

(vis));

//這裡別忘了初始化一次

ll tot_a=

change_root

(root_a,root_a,roota_sum)

; ll tot_b=

change_root

(root_b,root_b,rootb_sum)

; ll jie=tot_a/

2+tot_b/2+

(roota_sum+sum_num[root_a]+1

)*(sum_num[root_b]+1

)+rootb_sum*

(sum_num[root_a]+1

);cout

}

樹形 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入門

本題是要你任選乙個點為根使得樹上的所有點深度之和最小。本題是要你任選乙個點為根使得樹上的所有點深度之和最小。首先考慮如果是指定根節點你會不會做 已知根節點的話,我們只需要一遍dfs 或者bfs 就可以求出每個點的深度,然後求和就可以了。然後我們考慮如果我們知道當前節點x為根的結果能否快速求出以它某兒...