圣诞节快到了,蒜头君准备做一棵大圣诞树。
这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 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
思路装模做样,不堪一击
随便画个图
Ans = (20+30+40) * 3 = 40 * 1+30*2
笑死 = 20 * 3+30 * 5+40 * 4 = 370
很显然, n 节点与1联通的cost为 d[n] (最短路) * S[n] (节点权值)
当然证明也是很简单的
设节点n , 很显然n的权值会被贡献给它的所有祖先权值 , 所以 C = S[n] * (d[n]) (即其所有祖先与1联通权值和即最短路 ( 结合律不多说
// 如此拉跨的题解,就连我都不心潮澎湃 (
代码
using namespace std;
// C(J) = D[J] * w[j] * S[j] (fixed);
const int maxn = 5e4 + 10;
int n, m;
int S[maxn];
struct edge {
int v, w;
};
set< pair< long long, int > > min_heap;
vector< edge > G[maxn];
long long d[maxn];
void dij() {
min_heap.insert({0, 1});
d[1] = 0;
while (min_heap.size()) {
int u = min_heap.begin()->second;
min_heap.erase(min_heap.begin());
for (int i = 0; i < G[u].size(); ++i) {
int v = G[u][i].v, w = G[u][i].w;
if (w + d[u] < d[v]) {
min_heap.erase({d[v], v});
d[v] = w + d[u];
min_heap.insert({d[v], v});
}
}
}
}
const long long inf = 0x3f3f3f3f3f3f3f3f;
int main() {
freopen("chris.in", "r", stdin);
freopen("chris.out", "w", stdout);
memset(d, 0x3f, sizeof(d));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &S[i]);
}
for (int i = 1; i <= m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
G[u].push_back({v, w});
G[v].push_back({u, w});
}
dij();
long long ans = 0;
for (int i = 1; i <= n; ++i) {
if (d[i] == inf) {
printf("No Answer");
return 0;
}
ans += d[i] * S[i];
}
printf("%lld", ans);
}