zoukankan      html  css  js  c++  java
  • 【转】HDU-6035-Colorful Tree

    转自http://blog.csdn.net/Bahuia/article/details/76141574

    题意:

    题目链接: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的高度最高一批结点为根的子树大小总和。

    代码:

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 const int MAXN = 2e5 + 10;
      4 typedef long long LL;
      5 
      6 //题解地址
      7 //http://blog.csdn.net/Bahuia/article/details/76141574
      8 LL color[MAXN], sz[MAXN], sum[MAXN], vis[MAXN]; //sum[i]记录的是到达当前节点时,颜色为i的高度最高一批结点为根的子树大小总和
      9 //sz:全部节点数
     10 vector <int> tree[MAXN];
     11 LL ans;
     12 
     13 LL dfs(int u, int pa) {
     14     sz[u] = 1;
     15     LL allson = 0;
     16     int cnt = tree[u].size();
     17     for (int i = 0; i < cnt; i++) {
     18         int v = tree[u][i];
     19         if (v == pa) continue;
     20         LL last = sum[color[u]];            // 保存递归之前的sum值
     21         sz[u] += dfs(v, u);
     22         LL add = sum[color[u]] - last;      // add就是结点v为根的子树中颜色为color[u]且高度最高的若干子树的大小
     23         // 对于结点v来说,sz[v]-add就是v这棵子树最上端的,且不包含颜色为color[u]的连通块大小
     24         ans += (sz[v] - add) * (sz[v] - add - 1) / 2;  // 对这个连通块中任意两个点的路径都不包含颜色color[u]
     25         allson += sz[v] - add;          // allson记录下儿子结点v组成的不含颜色color[u]的连通块大小总和
     26     }
     27     sum[color[u]] += allson + 1;        // sum更新,此时要加上不含color[u]连通块的大小总和以及u自己
     28     return sz[u];
     29 }
     30 
     31 int main() {
     32     //freopen("data2.in", "r", stdin);
     33     int n, cs = 0;
     34     while (scanf("%d", &n) !=EOF) {
     35         memset(vis, 0, sizeof(vis));
     36         memset(sum, 0, sizeof(sum));
     37         int cnt = 0;
     38         for (int i = 1; i <= n; i++) {
     39             scanf("%I64d", &color[i]);
     40             if (!vis[color[i]]) ++cnt;
     41             vis[color[i]] = 1;
     42             tree[i].clear();
     43         }
     44         for (int i = 1; i < n; i++) {
     45             int u, v;
     46             scanf("%d%d", &u, &v);
     47             tree[u].push_back(v);
     48             tree[v].push_back(u);
     49         }
     50         printf("Case #%d: ", ++cs);
     51         if (cnt == 1) {             // 只有一种颜色的特殊情况
     52             printf("%I64d
    ", (LL)n * (n - 1LL) / 2LL);
     53             continue;
     54         }
     55         ans = 0;
     56         dfs(1, -1);
     57         for (int i = 1; i <= n; i++) {      // 注意最后要对整棵树来补充所有颜色剩下的联通块
     58             if (!vis[i]) continue;
     59             else {
     60                 ans += (n - sum[i]) * (n - sum[i] - 1LL) / 2LL;//子树下的统计完了,然后统计非子树的
     61           }
     62         }
     63         printf("%I64d
    ", (LL)n * (n - 1LL) / 2LL * cnt - ans);
     64     }
     65     return 0;
     66 }
    View Code
  • 相关阅读:
    【luogu T34117 打油门】 题解
    【luogu P1774 最接近神的人_NOI导刊2010提高(02)】 题解
    【luogu P1462 通往奥格瑞玛的道路】 题解
    【luogu P3808 AC自动机(简单版)】 模板
    替罪羊树~讲解
    【luogu P3369 【模板】普通平衡树(Treap/SBT)】 模板 Scapegoat Tree
    【luogu P1801 黑匣子_NOI导刊2010提高(06)】 题解
    【luogu P2590 [ZJOI2008]树的统计】 题解
    【luogu P3398 仓鼠找sugar】 题解
    【luogu P3884 [JLOI2009]二叉树问题】 题解
  • 原文地址:https://www.cnblogs.com/liuzhanshan/p/7241795.html
Copyright © 2011-2022 走看看