Big Christmas Tree
题目链接
题目大意
挺有意思的一题。
题目大意是现在有一个图,n个点,m条边,现在需要建一棵树,规定1为根,每条边的权值是子树上的点的权值乘上边的权值,现在要让这棵树的代价最小,问最小代价。
题解
我们考虑每个点对总代价的贡献,发现: \[ 每个点的贡献=点权值*该点到根的路径和 \]
这样就很清楚了,要使总贡献最小,使路径最短就可以了。于是变成了裸的单源最短路。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define LL long long
#define maxn 100005
using namespace std;
int T,v,E,cnt,pre[maxn];
LL c[maxn],d[maxn];
struct edge
{
int u,v,next;
LL d;
};
edge e[maxn<<1];
void addedge(int u,int v,LL dis)
{
e[cnt].u=u; e[cnt].v=v; e[cnt].d=dis;
e[cnt].next=pre[u]; pre[u]=cnt++;
}
void spfa(int st)
{
queue<int> q;
while (!q.empty()) q.pop();
bool vis[maxn];
memset(vis,0,sizeof(vis));
q.push(st); vis[st]=1; d[st]=0;
while (!q.empty())
{
int u=q.front(); q.pop();
vis[u]=0;
for (int i=pre[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if (d[v]==-1 || d[v]>d[u]+e[i].d)
{
d[v]=d[u]+e[i].d;
if (!vis[v])
{
vis[v]=1; q.push(v);
}
}
}
}
}
int main()
{
scanf("%d",&T);
while (T--)
{
memset(c,0,sizeof(c));
memset(e,0,sizeof(e)); cnt=0;
memset(d,-1,sizeof(d));
memset(pre,-1,sizeof(pre));
int a,b; LL dis;
scanf("%d%d",&v,&E);
for (int i=0;i<v;i++) scanf("%I64d",&c[i]);
for (int i=0;i<E;i++)
{
scanf("%d%d%I64d",&a,&b,&dis);
addedge(a,b,dis); addedge(b,a,dis);
}
spfa(1);
LL ans=0; bool f=0;
for (int i=1;i<=v;i++)
{
if (d[i]==-1)
{
f=1;
break;
}
ans+=d[i]*c[i-1];
}
if (f) printf("No Answer\n");
else printf("%I64d\n",ans);
}
return 0;
}