hdu6035

题意

给出一棵树,现在定义两点之间距离为两点间最短路径上颜色集合的大小。问任意两点间距离之和。

分析

换个方向,题目其实等价于求每种颜色在多少条路径上出现过(每种颜色对于答案的贡献),然后求和。

直接求不好求,但是我们可以求每种颜色在多少条路径上没有出现过,对于颜色 \(a\),我们删掉所有颜色为 \(a\) 的节点,那么树会被分成一个个树块或单个节点,那么一个大小为 \(3\) 的树块,显然有 \(3\) 条路径不包含颜色 \(a\),求和即可。实际上借助这个思想,而不用真的这么做。

设 \(sons[u]\) 数组为以 \(u\) 为根节点的子树的大小,\(sum[i]\) 表示颜色 \(i\) 已经合并的树块的大小。(比如说颜色为 a 的某个节点已经合并了它的两个不同颜色的子节点,那么如果它的父亲节点或上面的节点颜色也为 a ,那么递归结束回到上面的时候就要排除下面那两个不同颜色节点的影响,所以要合并到颜色 a 里面去)

\(s\) 记录在向上合并的过程中某个颜色在其未出现的块里能形成多少条路径。

设 \(u\) 是 \(v\) 的父亲节点,ct = sons[v] - sum[c[u]] + pre 计算的是从当前点 \(v\) 到下面每条链下最近的颜色为 \(c[u]\) 的节点之间的节点的数量,说明这些点还未合并到 \(c[u]\) 这个颜色里面,所以算出路径数累计到 \(s\) 里面,其中,\(sons[v]\) 可以理解成以 \(v\) 为根的子树的大小 , \(sum[c[u]]\) 表示下面 \(c[u]\) 这个颜色已经合并多少节点了,注意到 \(pre\) 的初始值为 \(sum[c[u]]\) ,也就是说 \(- sum[c[u]] + pre\) 保证我们算的是当前这颗子树下的未被合并的节点数量(因为我们前面可能先遍历了其它子树)。

最后,除了颜色 \(c[1]\) 能全部合并完( \(sum[c[1]] = n\) ,因为我们从 \(1\) 开始向下 \(DFS\) ),其它的可能未完全合并(节点 \(1\) 可能有多个子节点),\(n - sum[i]\) 为颜色 \(i\) 还未合并的节点的数量(且这些节点一定是连在一起的)。

合并的意思不是说某个颜色合并了这个块,其它颜色不能合并了,事实上每种颜色都要向上合并,最终只有颜色 \(c[1]\) 能合并完,那是因为我们是从 \(1\) 开始向下 \(DFS\) 。合并的意思也可以理解为:\(a\) 这个颜色合并了它下面的块,那么它及它下面的块对于所有上面颜色为 \(a\) 的节点都无意义了(想想前面通过删掉相同颜色的节点对树分块的思想)。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 10;
int kase = 1;
int n, c[MAXN];
int vis[MAXN];
int head[MAXN << 1], cnt;
struct Edge {
int to, next;
}e[MAXN << 1];
void addedge(int u, int v) {
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt++;
}
ll s;
ll sum[MAXN]; // 合并下面的节点成块
int sons[MAXN];
void dfs(int fa, int u) {
sons[u] = 1;
sum[c[u]]++;
ll pre = sum[c[u]];
for(int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if(v != fa) {
dfs(u, v);
sons[u] += sons[v];
ll ct = sons[v] - sum[c[u]] + pre;
s += 1LL * ct * (ct - 1) / 2;
sum[c[u]] += ct;
pre = sum[c[u]];
}
}
}
int main() {
while(~scanf("%d", &n)) {
memset(head, -1, sizeof head); cnt = 0;
s = 0;
memset(sum, 0, sizeof sum);
memset(vis, 0, sizeof vis);
int num = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &c[i]);
if(!vis[c[i]]) {
num++;
vis[c[i]] = 1;
}
}
for(int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs(0, 1);
ll ans = 1LL * num * n * (n - 1) / 2 - s;
for(int i = 1; i <= n; i++) {
if(vis[i]) {
ll ct = n - sum[i];
ans -= ct * (ct - 1) / 2;
}
}
printf("Case #%d: %lld\n", kase++, ans);
}
return 0;
}

hdu6035(树形DP)的更多相关文章

  1. hdu6035 Colorful Tree 树形dp 给定一棵树,每个节点有一个颜色值。定义每条路径的值为经过的节点的不同颜色数。求所有路径的值和。

    /** 题目:hdu6035 Colorful Tree 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题意:给定一棵树,每个节点有一个颜色值.定 ...

  2. HDU-6035 Colorful Tree(树形DP) 2017多校第一场

    题意:给出一棵树,树上的每个节点都有一个颜色,定义一种值为两点之间路径中不同颜色的个数,然后一棵树有n*(n-1)/2条 路径,求所有的路径的值加起来是多少. 思路:比赛的时候感觉是树形DP,但是脑袋 ...

  3. 回滚树形dp(按dfs序dp)——hdu6035

    本题前面的操作别的博客里都有.难点在于颜色ci的贡献,如何一次dfs求出答案 先来考虑如何在一次dfs中单独对颜色i进行计算 用遍历dfs序的方式,在深搜过程中,碰到带有颜色 i 的点 u,u每个颜色 ...

  4. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  5. COGS 2532. [HZOI 2016]树之美 树形dp

    可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...

  6. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

  7. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)

    题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...

  8. 树形DP

    切题ing!!!!! HDU  2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...

  9. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  10. POJ2342 树形dp

    原题:http://poj.org/problem?id=2342 树形dp入门题. 我们让dp[i][0]表示第i个人不去,dp[i][1]表示第i个人去 ,根据题意我们可以很容易的得到如下递推公式 ...

随机推荐

  1. Pascal小游戏 贪吃蛇

    一段未完成的Pascal贪吃蛇 说这段代码未完成其实是没有源代码格式化,FP中一行最多只有255字符宽. uses crt; const screenwidth=50; screenheight=24 ...

  2. heat launch an instance

    在包含Orchestration服务的环境中,可以创建启动实例的堆栈 创建yam文件 heat_template_version: 2015-10-15 description: Launch a b ...

  3. C++ Primer 第3章 字符串、向量和数组

    C++ Primer 第3章 字符串.向量和数组 C Primer 第3章 字符串向量和数组 1 命名空间的using声明 2 标准库类型string 3 标准库类型vector 4 迭代器介绍 5 ...

  4. 【多线程学习(2)】继承Thread类和实现Runnable接口、Callable接口的区别

    1)Runnable和Callable同是接口 * Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void);call方法可以抛出异常,run方法不可以 * 运行Cal ...

  5. golang中从一个日期开始往后增加一段时间

    废话少说上code, 这个是从当前日期开始,往后增加一个月时间 package main import ( "fmt" "time" ) func main() ...

  6. HDU 3954 Level up (线段树特殊懒惰标记)

    http://blog.csdn.net/acm_cxlove/article/details/7548087 感觉最巧的是定义了min_dis……将区间内有无英雄升级分开处理 #include &l ...

  7. 搭建springmvc项目404,没扫描到包

    搭建简单项目完成之后,曾经出现过一个问题 跳转报了404,控制台忘了没留啊... 反正意思就是说我配置有问题,导致没有扫描到注释的类 <context:component-scan base-p ...

  8. 201621123034 《Java程序设计》第8周学习总结

    作业08-集合 1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 答 ...

  9. (总结)Nginx 502 Bad Gateway错误触发条件与解决方法

    一些运行在Nginx上的网站有时候会出现“502 Bad Gateway”错误,有些时候甚至频繁的出现.以下是从Google搜集整理的一些Nginx 502错误的排查方法,供参考: Nginx 502 ...

  10. zkCli.sh客户端其他命令简介

    quit 退出 connect host:port 连接到其他ZooKeeper服务器 close 关闭连接 history 操作指令历史 redo 重复执行某个指令, 使用格式为 redo  id, ...