这题是一个有向图,因为他必须要从高往低,因此我们在建边时注意一下。这不是普通的最小生成树
因为有向图的叫最小树形图,但是我们可以用这个思想来做。首先把所有满足条件的边加到结构体中
其次,先做一下bfs遍历标记所有能到的点,之后把结构体按出边的高度为第一排序,按权值为第二排序
这么做的原因是,首先我们要保证最多的点,因此肯定所有点都要遍历到,其次,因为都是从高点像低点走。
所以其实是从1一层一层往外,只有高度高的先走了,高度低的才能走。也就是我们在kruscal时要求a-b这条边,a必须已经在集合中才可以。
其次这样排序不会影响答案,因为最后所有点的都会在集合中,所以a-b这条边如果在a已经加入集合的情况下还是最小的,他还是会被加入最小生成树。
排完序后做一遍生成树即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<functional> #include<string> #include<algorithm> #include<iostream> #include<set> #include<vector> #include<queue> #include<cstdlib> using namespace std; typedef long long ll; const int N=2e6+10; int a[N]; int h[N],ne[N],e[N],w[N],idx; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } struct node{ int x,b,c; bool operator <(const node &t) const{ if(a[b]!=a[t.b]) return a[b]>a[t.b]; return c<t.c; } }s[N]; int n,m; int cnt=1; int vis[N]; void bfs(){ queue<int> q; q.push(1); vis[1]=1; while(q.size()){ int t=q.front(); q.pop(); int i; for(i=h[t];i!=-1;i=ne[i]){ int j=e[i]; if(!vis[j]){ vis[j]=1; q.push(j); } } } } int p[N]; int find(int x){ if(x!=p[x]){ p[x]=find(p[x]); } return p[x]; } ll kruscal(){ sort(s+1,s+1+cnt); ll res=0; int i; for(i=1;i<=n;i++) p[i]=i; for(i=1;i<cnt;i++){ int x=s[i].x,b=s[i].b,c=s[i].c; if(!vis[x]||!vis[b]) continue; int pa=find(x),pb=find(b); if(pa!=pb){ p[pa]=pb; res+=c; } } return res; } int main(){ int i; cin>>n>>m; memset(h,-1,sizeof h); for(i=1;i<=n;i++){ scanf("%d",&a[i]); } for(i=1;i<=m;i++){ int u,v,c; scanf("%d%d%d",&u,&v,&c); if(a[u]>=a[v]){ s[cnt++]=node{u,v,c}; add(u,v,c); } if(a[v]>=a[u]){ add(v,u,c); s[cnt++]=node{v,u,c}; } } bfs(); ll res=kruscal(); int ans=0; for(i=1;i<=n;i++){ if(vis[i]) ans++; } cout<<ans<<" "<<res<<endl; }