codeforces 391E2 (【Codeforces Rockethon 2014】E2)
題意:有三棵樹。每棵樹有ni個結點,加入兩條邊把這三棵樹連接起來,合並成一棵樹。使得合並的樹中隨意兩點之間的最短路徑
的和最大。
分析:
三棵樹要合並成一棵樹,則第一棵樹必須選擇一個點,如果為X。第二棵樹必須選擇兩個點,如果為Y1, Y2,第三棵樹必須選擇一個點,如果為Z
記第一棵樹中全部結點到X的路徑總和為:tot1
第二棵樹中全部結點到Y1,Y2的路徑總和分別為:tot2, tot3
第三棵樹中全部結點到Z的路徑總和為:tot4;
共同擁有四種情況:
1,每棵樹內部的結點之間的距離為常數。能夠求出樹中一個點到剩余全部點的路徑之和,把全部這種點的和相加再除以2就可以
2,第一棵樹和第二棵樹這兩棵樹全部結點之間的距離,如果第一棵樹選擇結點X,第二棵樹選擇的左結點位Y1,
則兩棵樹上隨意兩點a,b之間的距離。能夠表示為:d(a, b) = d(a, X) + 1 + d(b, Y1),
當中a為第一棵樹的隨意結點。b為第二棵樹的隨意結點。
固定點a,變換bj。因為第二棵樹有n2個結點,則這樣的情況下的總的路徑和為:(d(a, X) + 1) * n2 + sum(d(bj, Y1), j = 1, 2, ..., n2);
再變換ai,則終於得到的路徑和為:sum((d(ai, X) + 1) * n2 + sum(d(bj, Y1), j = 1, 2, ..., n2), i = 1, 2, ..., n1);
終於結果為:sum(d(ai, X), i = 1, 2, ..., n1) * n2 + n2 * n1 + sum(d(bi, Y1), j = 1, 2, ..., n2) * n1;
即tot1 * n2 + n2 * n1 + tot2 * n1;
3,第二棵樹和第三棵樹這兩棵樹全部結點之間的距離,類似情況2,得到的終於結果為:tot3 * n3 + n2 * n3 + tot4 * n2;
4,第一棵樹和第三棵樹全部結點之間的距離:每一條路徑都能夠表示為:d(a, c) = d(a, X) + 1 + d(Y1, Y2) + 1 + d(Z, c);
終於結果為:tot1 * n3 + tot4 * n1 + n1 * n3 * d(Y1, Y2) + 2 * n1 * n3;
綜上所述,得到合並後樹中結點之間的距離總和為:
sum = (n2 + n3) * tot1 + (n1 + n2) * tot4 + n1 * n2 + n2 * n3 + 2 * n1 * n3 + n1 * tot2
+ n3 * tot3 + n1 * n3 * d(Y1, Y2) + 三棵樹的內部路徑;
要使得總和最大。則tot1和tot4必須最大,上式中間部分為常數,則left = n1 * tot2 + n3 * tot3 + n1 * n3 * d(Y1, Y2)必須達到最大
在tot2達到最大的情況下,即Y1確定時。枚舉Y2使得left部分達到最大,就可以。
這過程中要枚舉三棵樹的位置。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long LL; const int MAXN = 100000 + 10; struct Edge { int y, next; }; struct Tree { LL n, head[MAXN], nodeCnt[MAXN], edgeCnt, dis[MAXN], pos, maxTot; LL curSum[MAXN], tot[MAXN], pathCnt; Edge edge[MAXN << 1]; void addEdge(int x, int y) { edge[edgeCnt].y = y; edge[edgeCnt].next = head[x]; head[x] = edgeCnt++; } void build(int n) { memset(head, -1, sizeof(head)); this->n = n; int x, y; for(int i = 1; i < n; i++) { scanf("%d%d", &x, &y); addEdge(x, y); addEdge(y, x); } } /*獲得son這棵樹的結點數nodeCnt[son],包含該父結點。同一時候獲得son這棵樹中全部子結點到son的路徑之和,保存在curSum[son] 當中curSum[son] = sum(curSum[yi] + nodeCnt[yi], i = 1, 2, ...),即全部子樹的最短路徑值加上子樹的全部點數的和 */ void dfs0(int son, int fa) { nodeCnt[son] = 1; curSum[son] = 0; int y; for(int i = head[son]; i != -1; i = edge[i].next) { y = edge[i].y; if(y == fa) { continue; } dfs0(y, son); //回溯。已經獲得子結點y的值 nodeCnt[son] += nodeCnt[y]; curSum[son] += curSum[y] + nodeCnt[y]; } } //獲得tot[son],即全部點到son的路徑之和 void dfs1(int son, int fa, LL faLeft) { //當前son所在子樹的路徑之和,加上其它剩余部分到son的路徑之和 tot[son] = curSum[son] + faLeft; int y; for(int i = head[son]; i != -1; i = edge[i].next) { y = edge[i].y; if(y == fa) { continue; } /*要算全部結點到y的最短路徑之和。除了y所在子樹外,應該加入的值來源有三部分: son這棵樹原先應加上的值。即整棵大樹減去son子樹剩余部分到son的路徑和:faLeft, son這棵樹除了y這棵子樹全部結點到son的路徑值外剩余的路徑和: son這棵樹的最短路徑和 - y這棵樹的最短路徑和 - y這棵樹的結點數,即curSum[son] - curSum[y] - nodeCnt[y]; 整棵合並樹減去 y子樹剩余的結點數:n - nodeCnt[y] */ dfs1(y, son, faLeft + curSum[son] - curSum[y] - nodeCnt[y] + n - nodeCnt[y]); } } //深度遍歷,獲得每一個結點的層次,即為到根結點的最短路徑,註意根結點層次為0 void dfs2(int son, int fa) { dis[son] = dis[fa] + 1; int y; for(int i = head[son]; i != -1; i = edge[i].next) { y = edge[i].y; if(y == fa) { continue; } dfs2(y, son); } } void solve() { dfs0(1, 0); dfs1(1, 0, 0); //求出最大的單點最短路徑和,同一時候累加。即為這棵樹內部的路徑之和的兩倍 for(int i = 1; i <= n; i++) { pathCnt += tot[i]; if(tot[i] > maxTot) { maxTot = tot[i]; pos = i; } } dis[0] = -1; dfs2(pos, 0); } }; Tree t[3]; LL getAns(const Tree &t1, const Tree &t2, const Tree &t3) { //先算好不變的部分 LL tmp = (t2.n + t3.n) * t1.maxTot + (t1.n + t2.n) * t3.maxTot + t1.n * t2.n + t2.n * t3.n + 2 * t1.n * t3.n + (t1.pathCnt + t2.pathCnt + t3.pathCnt) / 2; LL ans, maxAns = 0; //固定Y1。t2.maxTot相當於tot2 tmp += t1.n * t2.maxTot; //枚舉Y2 for(int i = 1; i <= t2.n; i++) { //如果當前t2.tot[i]為tot3,t2.dis[i]為Y2到Y1的距離。Y1作為單原起點 ans = (LL)tmp + t3.n * t2.tot[i] + t1.n * t3.n * t2.dis[i]; maxAns = max(ans, maxAns); } return maxAns; } int main() { int n[3], i, j; LL ans = 0; //freopen("in.txt", "r", stdin); scanf("%d%d%d", &n[0], &n[1], &n[2]); for(i = 0; i < 3; i++) { t[i].build(n[i]); t[i].solve(); } //枚舉三棵樹的位置 for(i = 0; i < 3; i++) { for(j = 0; j < 3; j++) { if(i == j) { continue; } ans = max(ans, getAns(t[i], t[j], t[3 - i - j])); } } printf("%I64d\n", ans); return 0; }
參考博客:http://www.cnblogs.com/Delostik/p/3553114.html
codeforces 391E2 (【Codeforces Rockethon 2014】E2)