2019.01.17 bzoj2753: [SCOI2012]滑雪与时间胶囊(最小生成树)

传送门
最小生成树菜题。
题意:给出一些有向边,问有向的最小生成树。


思路:先dfsdfsdfs一把所有有用的边都存起来,然后按终点点权为第一关键字,边权为第二关键字给边排序保证最小生成树的合法性,排完之后跑kruskalkruskalkruskal就行了。
代码:

#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=2e5+5,M=2e6+5;
typedef long long ll;
typedef pair<int,int> pii;
int n,m,h[N],ans1=0,tot=0,fa[N];
bool vis[N];
vector<pii>e[N];
ll ans2=0;
struct edge{int u,v,w,c;}tt[M];
inline bool cmp(const edge&a,const edge&b){return a.c==b.c?a.w<b.w:a.c>b.c;}
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void dfs(int p){
	vis[p]=1,++ans1;
	for(ri v,i=0;i<e[p].size();++i){
		tt[++tot]=(edge){p,v,e[p][i].se,h[v=e[p][i].fi]};
		if(vis[v])continue;
		dfs(v);
	}
}
inline void kruskal(){
	sort(tt+1,tt+tot+1,cmp);
	for(ri i=1;i<=n;++i)fa[i]=i;
	for(ri cnt=0,i=1,fx,fy;i<=tot;++i){
		if(cnt==ans1-1)break;
		fx=find(tt[i].u),fy=find(tt[i].v);
		if(fx^fy)fa[fy]=fx,++cnt,ans2+=tt[i].w;
	}
}
int main(){
	n=read(),m=read();
	for(ri i=1;i<=n;++i)h[i]=read();
	for(ri i=1,u,v,w;i<=m;++i){
		u=read(),v=read(),w=read();
		if(h[u]>=h[v])e[u].push_back(pii(v,w));
		if(h[v]>=h[u])e[v].push_back(pii(u,w));
	}
	dfs(1);
	kruskal();
	cout<<ans1<<' '<<ans2;
	return 0;
} 

转载于:https://www.cnblogs.com/ldxcaicai/p/10367759.html

你可能感兴趣的:(2019.01.17 bzoj2753: [SCOI2012]滑雪与时间胶囊(最小生成树))