题目
分析
题目大意:
给定一个计算公式:某条边的边权=其所有子节点的点权和 × × 原始边权,以此生成一棵树,求最小边权和。
思路:
首先描述一下表达式:
ans=∑(u,v)subsum[v]⋅cost(u,v)
a
n
s
=
∑
(
u
,
v
)
s
u
b
s
u
m
[
v
]
⋅
c
o
s
t
(
u
,
v
)
;
我们考虑每个点对答案的贡献:对于某一点v,只有所有连接它的某一个祖先和该祖先的父亲的边,才会使用val[v]计算该边的权值。根据乘法分配律,也就是:
ans=∑vval[v]⋅cost(1,v)
a
n
s
=
∑
v
v
a
l
[
v
]
⋅
c
o
s
t
(
1
,
v
)
;cost(1,v)也就是根节点到v的最短路。
至于“No Answer”的情况,就是有一些点不与根节点连,跑完最短路后判断一下即可。
注意:要开long long!
代码
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int maxn=100002;
const long long inf=9223372036854775807;
struct Edge{
int to,next,v;
}e[maxn*2];
struct Node{
int a;
long long b;
bool operator < (const Node &A) const
{
return b>A.b;
}
};
int a[maxn],head[maxn];
long long dis[maxn];
bool vis[maxn];
int n,m,T,cnt;
long long ans;
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].v=w;
e[cnt].next=head[u];
head[u]=cnt;
}
bool dij()
{
priority_queue<Node>q;
for(int i=1;i<=n;i++)
dis[i]=inf,vis[i]=false;
dis[1]=0;
q.push(Node{1,0});
while(!q.empty())
{
Node u=q.top();
q.pop();
if(vis[u.a]) continue;
vis[u.a]=1;
for(int i=head[u.a];i;i=e[i].next)
{
int v=e[i].to;
if(dis[v]>dis[u.a]+e[i].v)
{
dis[v]=dis[u.a]+e[i].v*1ll;
q.push(Node{v,dis[v]});
}
}
}
for(int i=1;i<=n;i++)
if(dis[i]==inf)
return false;
return true;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
ans=0,cnt=0;
memset(head,0,sizeof(head));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
if(!dij())
{
printf("No Answer\n");
continue;
}
for(int i=1;i<=n;i++)
ans+=a[i]*dis[i]*1ll;
printf("%lld\n",ans);
}
}