计蒜客习题圣诞树

Posted mr94kevin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计蒜客习题圣诞树相关的知识,希望对你有一定的参考价值。

问题描述

圣诞节快到了,蒜头君准备做一棵大圣诞树。
这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 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<cctype>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 inline int get_num() { //读入优化
 7     int num;
 8     char c;
 9     while((c=getchar())==
||c== ||c==
);
10     num=c-0;
11     while(isdigit(c=getchar())) num=num*10+c-0;
12     return num;
13 }
14 void put_num(int i) { //输出优化
15     if(i>9) put_num(i/10);
16     putchar(i%10+0);
17 }
18 const int maxn=5e4+5,maxm=1e5+5,inf=0x3f3f3f3f;
19 int n,m,head[maxn],eid,d[maxn],vis[maxn],nw[maxn];
20 long long ans; //避免溢出
21 struct edge { //邻接表存储
22     int v,w,next;
23     edge(int v=0,int w=-1):v(v),w(w) {}
24 } E[maxm];
25 void init() { //记得初始化
26     memset(head,-1,sizeof(head));
27     memset(d,inf,sizeof(d));
28 }
29 void insert(int u,int v,int w) {
30     E[eid]=edge(v,w);
31     E[eid].next=head[u];
32     head[u]=eid++;
33 }
34 struct node { //自定义结构体保存结点
35     int n,s;
36     node(int n,int s):n(n),s(s) {}
37     bool operator < (const node& rhs) const { //重载小于运算符,使得最短路小的先出队
38         return s>rhs.s;
39     }
40 };
41 priority_queue<node> q;
42 void dijkstra(int s) {
43     d[s]=0;
44     q.push(node(s,0));
45     while(!q.empty()) {
46         int u=q.top().n;
47         q.pop();
48         if(vis[u]) continue;
49         vis[u]=1;
50         for(int p=head[u];p+1;p=E[p].next) {
51             int v=E[p].v;
52             if(d[v]>d[u]+E[p].w) {
53                 d[v]=d[u]+E[p].w;
54                 q.push(node(v,d[v]));
55             }
56         }
57     }
58 }
59 int main() {
60     n=get_num();
61     m=get_num();
62     for(int i=1;i<=n;++i) nw[i]=get_num();
63     init();
64     int a,b,c;
65     for(int i=1;i<=m;++i) {
66         a=get_num();
67         b=get_num();
68         c=get_num();
69         insert(a,b,c); //注意插入的应是双向边
70         insert(b,a,c);
71     }
72     dijkstra(1);
73     for(int i=1;i<=n;++i) {
74         if(d[i]==inf) {
75             printf("No Answer");
76             return 0;
77         }
78         ans+=nw[i]*d[i];
79     }
80     printf("%lld",ans);
81     return 0;
82 }
AC代码

 

以上是关于计蒜客习题圣诞树的主要内容,如果未能解决你的问题,请参考以下文章

计蒜客练习题:两仪剑法

计蒜客练习题:互质数个数

计蒜客习题 蒜头君的猜想(埃氏筛)

计蒜客练习题:质数原根

计蒜客习题 取石子游戏(gcd)

计蒜客习题蒜头君运送宝藏