zoukankan      html  css  js  c++  java
  • 【计蒜客习题】圣诞树

    问题描述

    圣诞节快到了,蒜头君准备做一棵大圣诞树。
    这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 1 到 n 编号,树的根永远是 1。每个结点都有一个自身特有的数值,称为它的权重,各个结点的权重可能不同。对于一棵做完的树来说,每条边都有一个价值 ve,若设这条边 e 连接结点 i 和结点 j,且 i 为 j的父结点(根是最老的祖先),则该边的价值ve=sj*we,sj表示结点 j 的所有子孙及它自己的权重之和,we表示边 e 的权值。
    现在蒜头君想造一棵树,他有 m 条边可以选择,使得树上所有边的总价值最小,并且所有的点都在树上,因为蒜头君喜欢大树。

    输入格式

    第一行输入两个整数 n 和 m(0≤n,m≤50,000),表示结点总数和可供选择的边数。
    接下来输入一行,输入 n 个整数,依次表示每个结点的权重。
    接下来输入 m 行,每行输入 3 个正整数a,b,c(1≤a,b,≤n,1≤c≤10,000),表示结点 a 和结点 b 之间有一条权值为 c 的边可供造树选择。

    输出格式

    输出一行,如果构造不出这样的树,请输出No Answer,否则输出一个整数,表示造树的最小价值。

    样例输入

    4 4
    10 20 30 40
    1 2 3
    2 3 2
    1 3 5
    2 4 1

    样例输出

    370


    其实这里就牵扯到最短路的一类问题,这类问题看似是生成树问题但其实是最短路问题,原因就在于边权的定义方式。先来想一种简单的情况,点的边权为1,或者说点没有边权,对于<i,j>(i是j的父亲),ve=cntj*we(cnt是j所在子树的结点个数),我们可以用单源最短路算法求出所有结点到树根的最短路,所有结点最短路之和就是答案。为什么呢?题目要求构造一棵树,那么考虑从树根分别走到各个结点的路径长度之和,则<i,j>的贡献就是cntj*we,因为每要走到j的子树中的一个点就要经过一次<i,j>,而这刚好是边权的定义。那么如何最小化整棵树的边权之和呢?其实就是最小化树根到每个结点的路径长度之和,如果树根确定,只需以树根为源点,跑一遍单源最短路,然后将各个结点的最短路累加起来,如果树根不确定,就需要对每个树根都求一遍到其他结点最短路之和,取最小值。回到原题,每个结点都有权值,其实也很好想,每条边对于答案的贡献都扩大了,比如某一结点的权值为w,先只考虑到这个点的路径,都相当于由原来只经过一次变为经过w次,也就是说,可以把权值为w的点看成没有权值的w个在相同位置的点,在统计答案时,只需将ans+=d[i]修改为ans+=w[i]*d[i]。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <queue>
     4 
     5 using namespace std;
     6 
     7 inline int get_num() {
     8     int num = 0;
     9     char c = getchar();
    10     while (c < '0' || c > '9') c = getchar();
    11     while (c >= '0' && c <= '9')
    12         num = num * 10 + c - '0', c = getchar();
    13     return num;
    14 }
    15 
    16 const int mmax = 5e4 + 5, inf = 0x3f3f3f3f;
    17 
    18 int head[mmax], eid;
    19 
    20 struct Edge {
    21     int v, w, next;
    22 } edge[2 * mmax];
    23 
    24 inline void insert(int u, int v, int w) {
    25     edge[++eid].v = v;
    26     edge[eid].w = w;
    27     edge[eid].next = head[u];
    28     head[u] = eid;
    29 }
    30 
    31 struct node {
    32     int id, dist;
    33     node(int i, int d) : id(i), dist(d) {}
    34     bool operator < (const node& rhs) const {
    35         return dist > rhs.dist;
    36     }
    37 };
    38 
    39 priority_queue<node> q;
    40 
    41 int v[mmax], dist[mmax], vis[mmax];
    42 
    43 inline void dijkstra() {
    44     memset(dist, inf, sizeof(dist));
    45     dist[1] = 0;
    46     q.push(node(1, 0));
    47     while (!q.empty()) {
    48         int u = q.top().id;
    49         q.pop();
    50         if (vis[u]) continue;
    51         vis[u] = 1;
    52         for (int p = head[u]; p; p = edge[p].next) {
    53             int v = edge[p].v, w = edge[p].w;
    54             if (dist[v] > dist[u] + w) {
    55                 dist[v] = dist[u] + w;
    56                 q.push(node(v, dist[v]));
    57             }
    58         }
    59     }
    60 }
    61 
    62 int main() {
    63     int n = get_num(), m = get_num();
    64     long long ans = 0;
    65     for (int i = 1; i <= n; ++i) v[i] = get_num();
    66     for (int i = 1; i <= m; ++i) {
    67         int a = get_num(), b = get_num(), c = get_num();
    68         insert(a, b, c);
    69         insert(b, a, c);
    70     }
    71     dijkstra();
    72     for (int i = 1; i <= n; ++i) {
    73         if (dist[i] == inf) {printf("No Answer"); return 0;}
    74         else ans += dist[i] * v[i];
    75     }
    76     printf("%lld", ans);
    77     return 0;
    78 }
    AC代码
  • 相关阅读:
    windows下使用vscode编写运行以及调试C/C++
    nginx基础模块
    Windows下配置nginx+php(wnmp)
    快速创建 Vue 项目
    你真的会玩SQL吗?冷落的Top和Apply
    你真的会玩SQL吗?透视转换的艺术
    你真的会玩SQL吗?你所不知道的 数据聚合
    你真的会玩SQL吗?简单的数据修改
    你真的会玩SQL吗?表表达式,排名函数
    你真的会玩SQL吗?Case也疯狂
  • 原文地址:https://www.cnblogs.com/Mr94Kevin/p/9515268.html
Copyright © 2011-2022 走看看