问题描述
圣诞节快到了,蒜头君准备做一棵大圣诞树。
这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 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
思路:
由于所有点都要在树上,所以可以计算每个点的权值来代替边的权值。对一个点i来说,在计算其与根节点之间每条边的权值时都要计算它,总和为dmin*si,其中dmin表示该点到根的最短距离。由于点与点直接不互相影响,所以只需让i到根的距离最小便可,且每个点都可这样处理。于是问题转化为从根节点出发的最短路。
注意,若有点不能到达,说明无法构造。
代码:
#include
using namespace std;
const int N=50010; const int INF=0x7ffffff;
struct Way{
int to,next,val;
} way[N<<1];
int n,m,cnt,a,b,c,cur,head[N],weight[N];
long long ans,dis[N];
void add(int u,int v,int w){
way[++cnt].next=head[u];
way[cnt].to=v; way[cnt].val=w;
head[u]=cnt;
}
struct cmp{
bool operator()(const int &a,const int &b){
if(dis[a]!=dis[b]) return dis[a]s;
void dijkstra(int st){
for(int i=1;i<=n;i++) dis[i]=INF;
dis[st]=0;
s.insert(st);
for(int i=1;i<=n;i++){
if(!s.size()){
printf("No Answer"); return;
}
cur=*s.begin(); s.erase(cur);
for(int j=head[cur];j;j=way[j].next)
if(dis[way[j].to]>dis[cur]+way[j].val){
s.erase(way[j].to);
dis[way[j].to]=dis[cur]+way[j].val;
s.insert(way[j].to);
}
}
for(int i=1;i<=n;i++)
ans+=dis[i]*weight[i];
printf("%lld",ans);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&weight[i]);
for(int i=1;i<=m;i++){
scanf("%d %d %d",&a,&b,&c);
add(a,b,c); add(b,a,c);
}
dijkstra(1);
return 0;
}