这个题关键在于怎么处理这些点需要重复到达的问题 题目要求最后只剩一个树图 即只留(n-1)条边 我们把每条边的边权乘二 再加上左右两节点的点权 作为新的边权 然后套克鲁斯卡尔 最后加上一个最小的点权
1. 边权乘二
那么对于每一条边edge[i] 它都连接了以左节点edge[i].u为根的子树(左树)和以右节点edge[i].v为根的子树(右树) 而题目要求最后必须回到出发点 那么假设我们出发点在左树 现在要经过edge[i]去遍历右树上的节点 去时走了一次edge[i] 因为要回到出发点 所以又走了一次edge[i] 所以这个一来一回是必不可少的 又因为我们返回时再次经过edge[i]时 肯定已经把右树节点都处理完毕(不然你回来干什么。。) 所以每条边必走两次且只走两次
2. 加上左右两节点的点权
设节点p 其邻接边有 a b c d
如果我们走a来到了p(一次访问) 现在要通过b c d去遍历其下的子树 因为要回到出发点 所以在回程时必经a各一次(b c d共三次)
所以每个节点有多少邻接点 即度为多少 那它就要被经过几次
可这些度是谁提供的呢 当然是边啊 无向图中每条边会为左右两个节点各贡献一个度 所以在这里我们就可以把这些点权附加到边权上了 这样就可以处理了
3. 加上一个最小的点权
出发时还要安慰出发点的那头牛 等于经过了一次出发点 但我们是突然出现在出发点的 无法在出发点的度上体现出来 所以必须单独考虑
不管我们把谁作为出发点 1 2条是不变的 所以哪个点权值小就把谁作为出发点
好啰嗦。。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 0x3f3f3f3f3f3f3f3f
struct node
{
int u;
int v;
ll w;
};
node edge[100010];
ll val[10010];
int f[10010];
int n,m;
bool cmp(node n1,node n2)
{
return n1.w<n2.w;
}
int getf(int p)
{
if(f[p]==p) return p;
else
{
f[p]=getf(f[p]);
return f[p];
}
}
bool unite(int u,int v)
{
int fu,fv;
fu=getf(u);
fv=getf(v);
if(fu!=fv)
{
f[fv]=fu;
return true;
}
else return false;
}
int main()
{
ll sum,minn;
int i,cnt;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%lld",&val[i]);
}
for(i=1;i<=m;i++)
{
scanf("%d%d%lld",&edge[i].u,&edge[i].v,&edge[i].w);
edge[i].w=2*edge[i].w+val[edge[i].u]+val[edge[i].v];
}
for(i=1;i<=n;i++)
{
f[i]=i;
}
sort(edge+1,edge+m+1,cmp);
sum=0,cnt=0;
for(i=1;i<=m;i++)
{
if(unite(edge[i].u,edge[i].v))
{
sum+=edge[i].w,cnt++;
}
if(cnt==n-1) break;
}
minn=N;
for(i=1;i<=n;i++)
{
minn=min(minn,val[i]);
}
printf("%lld\n",sum+minn);
return 0;
}