计蒜客圣诞树

问题描述


圣诞节快到了,蒜头君准备做一棵大圣诞树。 

这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 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;
}

你可能感兴趣的:(计蒜客圣诞树)