我是看了這位大佬的部落格,感謝大佬他的部落格
題目描述:有兩顆樹,需要在這兩棵樹之間新增一條邊,這樣就變成了一棵樹,求這棵樹任意兩點之間的最小距離和
即$$\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為根的結果能否快速求出以它某兒...