题目:http://codeforces.com/problemset/problem/391/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部分达到最大,就可以。

这过程中要枚举三棵树的位置。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. using namespace std;
  5. typedef long long LL;
  6. const int MAXN = 100000 + 10;
  7. struct Edge
  8. {
  9. int y, next;
  10. };
  11. struct Tree
  12. {
  13. LL n, head[MAXN], nodeCnt[MAXN], edgeCnt, dis[MAXN], pos, maxTot;
  14. LL curSum[MAXN], tot[MAXN], pathCnt;
  15. Edge edge[MAXN << 1];
  16. void addEdge(int x, int y)
  17. {
  18. edge[edgeCnt].y = y;
  19. edge[edgeCnt].next = head[x];
  20. head[x] = edgeCnt++;
  21. }
  22.  
  23. void build(int n)
  24. {
  25. memset(head, -1, sizeof(head));
  26. this->n = n;
  27. int x, y;
  28. for(int i = 1; i < n; i++)
  29. {
  30. scanf("%d%d", &x, &y);
  31. addEdge(x, y);
  32. addEdge(y, x);
  33. }
  34. }
  35.  
  36. /*获得son这棵树的结点数nodeCnt[son],包含该父结点。同一时候获得son这棵树中全部子结点到son的路径之和,保存在curSum[son]
  37. 当中curSum[son] = sum(curSum[yi] + nodeCnt[yi], i = 1, 2, ...),即全部子树的最短路径值加上子树的全部点数的和
  38. */
  39. void dfs0(int son, int fa)
  40. {
  41. nodeCnt[son] = 1;
  42. curSum[son] = 0;
  43. int y;
  44. for(int i = head[son]; i != -1; i = edge[i].next)
  45. {
  46. y = edge[i].y;
  47. if(y == fa)
  48. {
  49. continue;
  50. }
  51. dfs0(y, son);
  52. //回溯。已经获得子结点y的值
  53. nodeCnt[son] += nodeCnt[y];
  54. curSum[son] += curSum[y] + nodeCnt[y];
  55. }
  56. }
  57.  
  58. //获得tot[son],即全部点到son的路径之和
  59. void dfs1(int son, int fa, LL faLeft)
  60. {
  61. //当前son所在子树的路径之和,加上其它剩余部分到son的路径之和
  62. tot[son] = curSum[son] + faLeft;
  63. int y;
  64. for(int i = head[son]; i != -1; i = edge[i].next)
  65. {
  66. y = edge[i].y;
  67. if(y == fa)
  68. {
  69. continue;
  70. }
  71. /*要算全部结点到y的最短路径之和。除了y所在子树外,应该加入的值来源有三部分:
  72. son这棵树原先应加上的值。即整棵大树减去son子树剩余部分到son的路径和:faLeft,
  73. son这棵树除了y这棵子树全部结点到son的路径值外剩余的路径和:
  74. son这棵树的最短路径和 - y这棵树的最短路径和 - y这棵树的结点数,即curSum[son] - curSum[y] - nodeCnt[y];
  75. 整棵合并树减去 y子树剩余的结点数:n - nodeCnt[y]
  76. */
  77. dfs1(y, son, faLeft + curSum[son] - curSum[y] - nodeCnt[y] + n - nodeCnt[y]);
  78. }
  79. }
  80.  
  81. //深度遍历,获得每一个结点的层次,即为到根结点的最短路径,注意根结点层次为0
  82. void dfs2(int son, int fa)
  83. {
  84. dis[son] = dis[fa] + 1;
  85. int y;
  86. for(int i = head[son]; i != -1; i = edge[i].next)
  87. {
  88. y = edge[i].y;
  89. if(y == fa)
  90. {
  91. continue;
  92. }
  93. dfs2(y, son);
  94. }
  95. }
  96.  
  97. void solve()
  98. {
  99. dfs0(1, 0);
  100. dfs1(1, 0, 0);
  101. //求出最大的单点最短路径和,同一时候累加。即为这棵树内部的路径之和的两倍
  102. for(int i = 1; i <= n; i++)
  103. {
  104. pathCnt += tot[i];
  105. if(tot[i] > maxTot)
  106. {
  107. maxTot = tot[i];
  108. pos = i;
  109. }
  110. }
  111. dis[0] = -1;
  112. dfs2(pos, 0);
  113. }
  114. };
  115.  
  116. Tree t[3];
  117.  
  118. LL getAns(const Tree &t1, const Tree &t2, const Tree &t3)
  119. {
  120. //先算好不变的部分
  121. 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
  122. + (t1.pathCnt + t2.pathCnt + t3.pathCnt) / 2;
  123. LL ans, maxAns = 0;
  124.  
  125. //固定Y1。t2.maxTot相当于tot2
  126. tmp += t1.n * t2.maxTot;
  127.  
  128. //枚举Y2
  129. for(int i = 1; i <= t2.n; i++)
  130. {
  131. //如果当前t2.tot[i]为tot3,t2.dis[i]为Y2到Y1的距离。Y1作为单原起点
  132. ans = (LL)tmp + t3.n * t2.tot[i] + t1.n * t3.n * t2.dis[i];
  133. maxAns = max(ans, maxAns);
  134. }
  135. return maxAns;
  136. }
  137.  
  138. int main()
  139. {
  140. int n[3], i, j;
  141. LL ans = 0;
  142. //freopen("in.txt", "r", stdin);
  143. scanf("%d%d%d", &n[0], &n[1], &n[2]);
  144. for(i = 0; i < 3; i++)
  145. {
  146. t[i].build(n[i]);
  147. t[i].solve();
  148. }
  149.  
  150. //枚举三棵树的位置
  151. for(i = 0; i < 3; i++)
  152. {
  153. for(j = 0; j < 3; j++)
  154. {
  155. if(i == j)
  156. {
  157. continue;
  158. }
  159. ans = max(ans, getAns(t[i], t[j], t[3 - i - j]));
  160. }
  161. }
  162. printf("%I64d\n", ans);
  163. return 0;
  164. }

參考博客:http://www.cnblogs.com/Delostik/p/3553114.html

codeforces 391E2 (【Codeforces Rockethon 2014】E2)的更多相关文章

  1. uoj 41 【清华集训2014】矩阵变换 婚姻稳定问题

    [清华集训2014]矩阵变换 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/41 Description 给出 ...

  2. AC日记——【清华集训2014】奇数国 uoj 38

    #38. [清华集训2014]奇数国 思路: 题目中的number与product不想冲: 即为number与product互素: 所以,求phi(product)即可: 除一个数等同于在模的意义下乘 ...

  3. 【Codeforces Rockethon 2014】Solutions

    转载请注明出处:http://www.cnblogs.com/Delostik/p/3553114.html 目前已有[A B C D E] 例行吐槽:趴桌子上睡着了 [A. Genetic Engi ...

  4. CodeForces - 686D 【树的重心】

    传送门:http://codeforces.com/problemset/problem/686/D 题意:给你n个节点,其中1为根, 第二行给你2~n的节点的父亲节点编号. 然后是q个询问,求询问的 ...

  5. CodeForces 604C 【思维水题】`

    题意: 给你01字符串的长度再给你一个串. 然后你可以在这个串中选择一个起点和一个终点使得这个连续区间内所有的位取反. 求: 经过处理后最多会得到多少次01变换. 例如:0101是4次,0001是2次 ...

  6. CodeForces 593D【树链剖分】

    题意: 给你n个点和n-1条边组成的一棵树,按顺序给出数的每一条边. 询问m次,每次给出一个x求x除以从点a到点b所有边的权值和的乘积,还有修改,给出边的编号,修改某条边的权值. 思路: 树链剖分,用 ...

  7. Codeforces | CF1000B 【Light It Up】

    蒟蒻第二篇题解... 比赛的时候写这道题MLE了qwq..根据CF的赛制我也没敢再交第二次.. 简单讲一下思路好了(假装是dalao)..根据题意要加一个或者不加新的点..如果加一个新的点意味着从这个 ...

  8. CodeForces 131D【图特性+BFS】

    题意: 只有一个环,然后环都是0(环缩点相当于树的根),然后其余的输出到根的距离 思路: 可以从度为1的 开始搜 把那些分支全标记掉,然后再取没有标记掉的,BFS一下搞出距离. 具体这个标记: 倒着搜 ...

  9. CodeForces 125D【鸽巢原理】

    哇塞?开始的三个数其中两个数一定能确定一个序列.(鸽巢原理) #include <bits/stdc++.h> using namespace std; typedef long long ...

随机推荐

  1. python抢票开发——设备预约助手实现

    女朋友是药学院的,做实验时需要在特定的网站上进行设备预约,由于预约人数过多,从而导致从浏览器登录不进去或者登录进去预约失败等情况,所以我用python帮她写了一个抢位助手,让程序自动去进行位置预定,实 ...

  2. 汇编程序45:检测点13.2 (loop指令的中断例程)

    安装程序: assume cs:code //loop指令的替代实现 code segment start: mov ax,cs mov ds,ax mov si,offset sub1 mov ax ...

  3. 【Codeforces】383.DIV2

    昨天一场CF发挥不好.抽点时间总结一下,然后顺带算是做个题解. 第一题水题 第二题思路很清晰,大概十分钟就想出来规模100000明显复杂度最多nlog所以只能一遍loop然后里利用map统计得到后面的 ...

  4. Mac使用bootcamp安装win8.1出现网卡驱动没有安装问题

    问题:没有网络连接 原因:在bootcamp烧的u盘里面其实附带了驱动,只是没有自动安装 解决:D:\BootCamp\Drivers\Broadcom\BroadcomWirelessWin8x64 ...

  5. SVN系列学习(一)-SVN的安装与配置

    1.SVN的介绍 SVN是Subversion的简称,是一个开发源代码的版本控制系统,采用了分支管理系统. 文件保存在中央版本库,除了能记住文件和目录的每次修改以外,版本库非常像普通的文件服务器.你可 ...

  6. [Codeforces]Codeforces Round #490 (Div. 3)

    Mishka and Contest #pragma comment(linker, "/STACK:102400000,102400000") #ifndef ONLINE_JU ...

  7. java web项目和java项目的区别(看清IDE本质)

    想必大家在使用MyEclipse时对这两个概念不去深究.只知道是Java EE类的基本都是Web项目,而Java应用程序就是Java项目.而且很多人都愿意使用MyEclipse作为开发工具,且不说大家 ...

  8. Python语言之数据结构1(序列--列表,元组,字符串)

    0.序列 列表,元组,字符串都是序列. 序列有两个特点:索引操作符和切片操作符.索引操作符让我们可以从序列中抓取一个特定项目.切片操作符让我们能够获取序列的一个切片,即一部分序列. 以字符串为例: 1 ...

  9. day13-迭代器、三元表达式、列表推导式、字典生成式、生成器与递归

    目录 迭代器 可迭代对象 迭代器对象 for循环原理 三元表达式(三目表达式) 列表推导式 字典生成式 zip()方法 生成器 生成器表达式 递归 递归的两个阶段 迭代器 迭代器即迭代的工具,迭代是一 ...

  10. Java 之jdbc连接mysql数据库

    package jdbc; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; ...