CF160D Edges in MST

CF160D Edges in MST

​ 首先考虑不可能构成最小生成树的边。我们先按边权排序,对于一条边 \(e\),我们将所有边权严格小于它的边相连,此时 \(n\) 个点构成了数个联通块,如果这条边两端的端点 \(u,v\) 已经在一个联通块里了,说明这条边根本用不上,也就是这条边是不可能构成最小生成树的边。

​ 接下来考虑肯定在最小生成树里面的边。对于一条边 \(e\),我们依然将所有边权严格小于它的边相连,然后考虑所有边权和他相等的边,将其他边权和他相等的边相连,如果此时 \(e\) 的两端仍然不在一个联通块内,说明 \(e\) 是肯定在最小生成树里面的边。

​ 但是如果暴力模拟的复杂度是 \(O(m^2)\) 的。通过观察可知,其实 \(e\) 就是原图中的割边。所以我们可以通过 Tarjan 算法求出割边。但是这样复杂度仍然不能保证,这样我们的复杂度是 \(w\times m\) 的。 \(w\) 表示不同边权的数量。考虑继续优化。

​ 假设我们现在在研究边权为 \(w\) 的边集 \(e\)。我们发现,我们所求的割边是要在边集 \(e\) 中的,如果每一次我们 Tarjan 的复杂度能够达到 \(O(siz(e))\) 那么总时间复杂度就变成了 \(O(m)\)。所以,可以将所有边权小于 \(w\) 的边相连,并将所形成的联通块缩点。那么我们对边集 \(e\) 进行处理时,复杂度就变成了 \(O(siz(e))\)。

代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1e5+5;
int n,m,f[MAXN],color[MAXN],ans[MAXN],dfn[MAXN],low[MAXN],totdfn;
int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}
struct edge
{
	int u,v,w,id;
	bool operator < (const edge&x)const
	{
		return w<x.w;
	}
}edge[MAXN];
struct E
{
	int to,id;
};
vector <E> e[MAXN];
void add(int i)
{
	int u=find(edge[i].u),v=find(edge[i].v),id=edge[i].id;
	dfn[u]=dfn[v]=0;
	e[u].push_back(E{v,id});
	e[v].push_back(E{u,id});
}
void Merge(int u,int v)
{
	f[v]=u;
	e[u].clear();e[v].clear();
}
void dfs(int p,int fa)
{
	dfn[p]=low[p]=++totdfn;
	bool flag=0;
	for(int i=0;i<e[p].size();++i)
	{
		int to=e[p][i].to;
		if(to==fa&&!flag)
		{
			flag=1;
			continue;
		}
		if(!dfn[to])
		{
			dfs(to,p);
			if(low[to]>dfn[p]) ans[e[p][i].id]=1;
			low[p]=min(low[p],low[to]);
		}
		else low[p]=min(low[p],dfn[to]);
	}
}
int main()
{
//	freopen("mstagain.in","r",stdin);
//	freopen("mstagain.out","w",stdout);
	scanf("%d %d",&n,&m);
	int MX=0;
	for(int i=1;i<=m;++i)
	{
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		edge[i].u=u,edge[i].v=v,edge[i].w=w;
		MX=max(MX,w);
		edge[i].id=i;
	}
	for(int i=1;i<=n;++i) f[i]=i;
	sort(edge+1,edge+1+m);
	int now=1;
	while(now<=m)
	{
		int p=now;
		while(p<m&&edge[p+1].w==edge[p].w)
			++p;
		for(int i=now;i<=p;++i)
		{
			if(find(edge[i].u)==find(edge[i].v))
				ans[edge[i].id]=2;
			else add(i);
		}
		for(int i=now;i<=p;++i)
		{
			if(find(edge[i].u)==find(edge[i].v)) continue;
			if(!dfn[find(edge[i].u)]) dfs(find(edge[i].u),0);
			if(!dfn[find(edge[i].v)]) dfs(find(edge[i].v),0);
		}
		for(int i=now;i<=p;++i)
			if(find(edge[i].u)!=find(edge[i].v)) Merge(find(edge[i].u),find(edge[i].v));
		now=p+1;
	}
	for(int i=1;i<=m;++i)
	{
		if(ans[i]==1) printf("any\n");
		else if(ans[i]==0) printf("at least one\n");
		else printf("none\n");
	}
	return 0;
}
上一篇:叮咚mst


下一篇:生成树和最小生成树