2017 Multi-University Training Contest - Team 1 1003&&HDU 6035 Colorful Tree【树形dp】
Colorful Tree
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 1539 Accepted Submission(s): 616
Sample Input
3
1 2 1
1 2
2 3
6
1 2 1 3 2 1
1 2
1 3
2 4
2 5
3 6
Sample Output
Case #1: 6
Case #2: 29
Source
2017 Multi-University Training Contest - Team 1
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035
题意:
一棵n个结点的树,每个结点都有颜色,定义两点之间的路径长度为路径上出现的不同颜色数目,求树上所有路径的长度和。
思路:
“真的难”系列。
官方题解:
首先这题肯定是算贡献,也就是计算出每种颜色参与了多少条路径,但这样正面考虑并不容易,不妨从反面考虑,计算每种颜色没有参与多少路径,然后拿 (路径总数 * 颜色总数) - 没参与的贡献,就是答案了。 对于一种颜色x,怎么计算没参与的路径数目呢,很显然,对于每个不包含颜色x的连通块中任意两点路径都是x不参与的贡献,那么问题就转化为,对于任意一种颜色x,需要求出每个不包含x的连通块大小。 这里利用了树形dp,对于结点u,若u的颜色为x,那么dfs(u)的过程中,我们就想知道对于u的每个儿子v构成的子树中最高的一批颜色也为x的结点是哪些,要是知道这些结点子树的大小,只要拿子树v的大小减去这些节点子树的大小,就可以得到包括v在内的没有颜色x的连通块大小。 举个例子,如图:
对于结点1,我们想求出与1相邻的且不是黑色的结点组成的连通块大小,此时就要dfs结点1的两个儿子2和3,若我们处理出来结点2中,最高的一批黑色结点(4,8)的所构成子树的大小分别为2和1,那么我们拿2为根子树的大小5,减去2,减去1,就得到了结点2不包含黑色结点的连通块大小是5-2-1=2,也就是图中的2和5组成的连通块。同理对3,可以求得连通块大小是2({3,6})。 计算出了子树u中连通块大小对答案的贡献之后,就要将当前最高一批的黑色结点更新为u,最高黑色节点构成的子树大小sum加上子树u中没有计算的那部分大小5({1,2,3,5,6})。 具体实现,看代码,sum[x]表示的遍历到当前位置,颜色为x的高度最高一批结点为根的子树大小总和。
下面给出AC代码:
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int MAXN = 2e5 + 10;
4 typedef long long LL;
5
6 LL color[MAXN], sz[MAXN], sum[MAXN], vis[MAXN];
7 vector <int> tree[MAXN];
8 LL ans;
9
10 LL dfs(int u, int pa) {
11 sz[u] = 1;
12 LL allson = 0;
13 int cnt = tree[u].size();
14 for (int i = 0; i < cnt; i++) {
15 int v = tree[u][i];
16 if (v == pa) continue;
17 LL last = sum[color[u]]; // 保存递归之前的sum值
18 sz[u] += dfs(v, u);
19 LL add = sum[color[u]] - last; // add就是结点v为根的子树中颜色为color[u]且高度最高的若干子树的大小
20 // 对于结点v来说,sz[v]-add就是v这棵子树最上端的,且不包含颜色为color[u]的连通块大小
21 ans += (sz[v] - add) * (sz[v] - add - 1) / 2; // 对这个连通块中任意两个点的路径都不包含颜色color[u]
22 allson += sz[v] - add; // allson记录下儿子结点v组成的不含颜色color[u]的连通块大小总和
23 }
24 sum[color[u]] += allson + 1; // sum更新,此时要加上不含color[u]连通块的大小总和以及u自己
25 return sz[u];
26 }
27
28 int main() {
29 //freopen("in.txt", "r", stdin);
30 int n, cs = 0;
31 while (scanf("%d", &n) !=EOF) {
32 memset(vis, 0, sizeof(vis));
33 memset(sum, 0, sizeof(sum));
34 int cnt = 0;
35 for (int i = 1; i <= n; i++) {
36 scanf("%I64d", &color[i]);
37 if (!vis[color[i]]) ++cnt;
38 vis[color[i]] = 1;
39 tree[i].clear();
40 }
41 for (int i = 1; i < n; i++) {
42 int u, v;
43 scanf("%d%d", &u, &v);
44 tree[u].push_back(v);
45 tree[v].push_back(u);
46 }
47 printf("Case #%d: ", ++cs);
48 if (cnt == 1) { // 只有一种颜色的特殊情况
49 printf("%I64dn", (LL)n * (n - 1LL) / 2LL);
50 continue;
51 }
52 ans = 0;
53 dfs(1, -1);
54 for (int i = 1; i <= n; i++) { // 注意最后要对整棵树来补充所有颜色剩下的联通块
55 if (!vis[i]) continue;
56 ans += (n - sum[i]) * (n - sum[i] - 1LL) / 2LL;
57 }
58 printf("%I64dn", (LL)n * (n - 1LL) / 2LL * cnt - ans);
59 }
60 return 0;
61 }
- 幻灯片jQuery插件Orbit 介绍(附添加到WordPress教程)
- Gravatar开发者手册
- Gravatar开发者手册
- 使用Google CDN服务提供的jQuery库
- 比特币的分叉币都认为能够取代比特币,事实真的是这样吗?
- Google官方网页载入速度检测工具PageSpeed Insights 使用教程
- ASP.NET 路由
- Kafka定时清除过期数据
- 腾讯高级副总裁郭凯天:打造腾讯智库分析互联网产业前沿问题
- Google Chrome 浏览器 开发者工具 使用教程
- 反向代理(Reverse Proxy)及 IIS 7 应用请求路由模块
- 2014腾讯“大数据连接的未来”高峰论坛在京召开
- 工作流、业务流程管理和SOA
- 面向对象设计的SOLID原则
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法