【计蒜客习题】圣诞树

问题描述

圣诞节快到了,蒜头君准备做一棵大圣诞树。
这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 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())==‘\n‘||c==‘ ‘||c==‘\r‘);
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代码

原文地址:https://www.cnblogs.com/Mr94Kevin/p/9515268.html

时间: 2024-11-04 13:51:16

【计蒜客习题】圣诞树的相关文章

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

!!!原来LCA的题可以出的这么难,完了这还属于水题?! 先来解释一下题意,有N个城市,在这N城市之间有M条边(不一定每个城市都有边).我们的任务是找出给定两个点之间路径上的最小边权,使得这个最小边权尽量大.一开始我很纳闷,这和LCA有什么关系呢,怎么和最大流有点像.冥思苦想(看了别人的想法)之后,哦,原来是最大生成树+LCA...因为是最大生成树,可以使得任意两点间路径上的最大边权最大(再连上较小的会成环).这样问题就变成了找树上两点间路径上的最小边权,显然可以利用LCA,这是他十分经典的应用

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

问题描述 蒜头君和花椰妹在玩一个游戏,他们在地上将 n 颗石子排成一排,编号为 1 到 n.开始时,蒜头君随机取出了 2 颗石子扔掉,假设蒜头君取出的 2 颗石子的编号为 a, b.游戏规则如下,蒜头君和花椰妹 2 人轮流取石子,每次取石子,假设某人取出的石子编号为 i,那么必须要找到一对 j, k 满足 i=j−k 或者 i=j+k ,并且编号为 j,k 的石子已经被取出了,如果谁先不能取石子了,则视为输了.蒜头君比较绅士,让花椰妹先手. 输入格式 第一行输入一个整数 t(1≤t≤500),表

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

解题思路: 从1到n将所有数筛一遍,枚举加数,如果这两个加数都是质数,那么方案数加1. AC代码: 1 #include<cstdio> 2 3 using namespace std; 4 5 int n,ans; 6 bool vis[8000001]; 7 8 int main() 9 { 10 scanf("%d",&n); 11 for(int i = 2;i * i <= n; i++) 12 if(!vis[i]) { 13 for(int j

计蒜客 无脑博士和他的试管们

无脑博士有三个容量分别是A,B,C升的试管,A,B,C分别是三个从1到20的整数,最初,A和B试管都是空的,而C试管是装满硫酸铜溶液的.有时,无脑博士把硫酸铜溶液从一个试管倒到另一个试管中,直到被灌试管装满或原试管空了.当然每一次灌注都是完全的.由于无脑博士天天这么折腾,早已熟练,溶液在倒的过程中不会有丢失. 写一个程序去帮助无脑博士找出当A是个是空的时候,C试管中硫酸铜溶液所剩量的所有可能性. 输入包括一行,为空格分隔开的三个数,分别为整数A,B和C. 输出包括一行,升序地列出当A试管是空的时

简单斐波那契——计蒜客(4)

题目来自“计蒜客”第4题. 解算法题之前,务必先写出与之对应的数学表达式,用于描述算法. 数学描述如图: 根据“数学描述“,写出代码如下: #include <stdio.h> int main() { int N =0 ; scanf("%d", &N); int i, fn1 = 1, fn2 = 0, fn; switch(N) { case 0: printf("0"); break; case 1: printf("1&quo

计蒜客普及组模拟赛

今天没事闲的看到计蒜客有个普及组模拟赛,就当练了练手去打了,成绩低的可怜...400分崩成280分AK梦想化作泡影 第一题 同学的爱好 链接:https://nanti.jisuanke.com/t/17291 小学应用题难度?大概画个图就能懂,把每个部分都标上号,算出a,b,c,d,e,f的部分,进行运算就行了. 不多解释了,直接上代码 #include<iostream> #include<cstdio> #include<algorithm> #include&l

计蒜客 作弊揭发者(string的应用)

鉴于我市拥堵的交通状况,市政交管部门经过听证决定在道路两侧安置自动停车收费系统.当车辆驶入车位,系统会通过配有的摄像头拍摄车辆画面,通过识别车牌上的数字.字母序列识别车牌,通过连接车管所车辆信息数据库确认车辆,进行扣费. 斗智斗勇的好戏一般从此处展开… 一些车主通过在停车时遮挡车牌上的一个或多个数字.字母序列,来阻碍识别系统的识别工作,以此逃避停车费用的缴纳. 车主这简直是用轻轻的一挡搞出来一个世界难题有木有?!管理是一方面,技术解决才是王道啊. 这么难的项目不得不交给计蒜客实验室了.D 神负责

计蒜客 删除字母&#39;c&#39;

删除字母'c' 右侧的程序实现的功能是从字符串s中删除所有的小写字母c,请改正程序错误的地方. 注意:main函数不可以改动,程序结构也不能修改. 很简单的哦,加油吧- 样例输入 abccabcn 样例输出 ababn 1 #include <stdio.h> 2 3 #define N 1024 4 5 void del(char *s) 6 { 7 int i, j; 8 for(i = j = 0; s[i] != '\0'; i++) 9 { 10 if(s[i] != 'c') 11

计蒜客 教科书般的亵渎

Description: 环境里有 nn 个怪物,他们的生命值用一个正整数表示.现在,你可以使用两种魔法,对怪物进行攻击.当怪物的生命值小于等于 00 时,他便被消灭了. 魔法箭,对摸个生物造成 kk 点伤害,对一个生物最多使用一次,但没有使用次数限制. 亵渎,对所有生物造成一点伤害,如果杀死了某个生物,则继续自动重新使用该法术.只能主动使用一次,且必须最后使用. 请问,最多能消灭多少个怪物?亵渎法术最多能释放几次? Input: 第一行两个整数 nn 和 kk ,表示怪物的数量和法术的伤害.第