最小生成树 (tarjan 求桥)

最小生成树
给你一个带权连通的无向图,对于最小生成树,大家都很熟悉了,我这里就不定义了。对于任意一个连通图,显然存在最小生成树,但是在通常情况下,一个图的最小生成树是不唯一的。你的任务就是:对于给你的图,来确定该图的每一条边是否在任意一棵最小生成树中,或者是至少在某一棵最小生成树中,或者是不在最小生成树中。
【输入格式】
输入的第一行含有两个整数n和m(2<=n<=1e5,n-1<=m<=min(e=1e5,n*(n-1)/2)),分别表示顶点数和边数。
接下来m行,描述这边的信息,格式为“ai bi wi”(1<=ai,bi<=n,1<=wi<=1e6,ai!=bi),ai和bi为第i条边的两个顶点,wi为这条边的权值。这图保证是连通的且不含有重边。
【输出格式】
输出m行信息,如果第i条边包含在任何一棵最小生成树中,那么输出“any”;如果第i条边至少包含在某一棵最小生成树中,那么输出“at least one”;如果第i条边不在任何一棵最小生成树中,那么输出“none”。按输入边的顺序给出答案。
【输入样例1】
4 5
1 2 101
1 3 100
2 3 2
2 4 2
3 4 1
【输出样例1】
none
any
at least one
at least one
any
【输出样例2】
3 3
1 2 1
2 3 1
1 3 2
【输出样例2】
at least one
at least one
at least one


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100003
using namespace std;
int n,m;
int point[N],next[N],v[N],c[N],tot,fa[N],pd[N];
int low[N],dfsn[N],st[N],top,q[N],size,sz,cnt;
int par[N],ins[N];
struct data
{
	int x,y,z;
	int num;
}a[N];
void add(int x,int y,int z)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
int cmp(data a,data b)
{
	return a.z<b.z;
}
int find(int x)
{
	if (fa[x]==x) return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
void tarjan(int x)
{
	ins[x]=1;
	low[x]=dfsn[x]=++sz;
	for (int i=point[x];i;i=next[i])
	 if (!ins[v[i]])
	 {
	 	 	par[v[i]]=c[i];
	 	 	tarjan(v[i]);
	 	 	low[x]=min(low[x],low[v[i]]);
	 }
	 else  if (c[i]!=par[x]) low[x]=min(low[x],dfsn[v[i]]);
	 if (low[x]==dfsn[x]&&par[x])  pd[par[x]]++;
	
}
int main()
{
	freopen("mst.in","r",stdin);
	freopen("mst.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	 scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].num=i;
	sort(a+1,a+m+1,cmp);
	for (int i=1;i<=n;i++)
	 fa[i]=i;
	int i=1; int num=0;
	while (i<=m)
	{
		if (a[i].z>a[i-1].z)
		{
			int j=i;
			while (a[j].z==a[i].z&&j<=m)
			 j++;
			j--; tot=0; size=0; top=0; sz=0; cnt=0;
			for (int k=i;k<=j;k++)
			{
				int r1=find(a[k].x); int r2=find(a[k].y);
				if (r1!=r2)
				{
					pd[a[k].num]=1;
					point[r1]=0; point[r2]=0; dfsn[r1]=0; dfsn[r2]=0;
					low[r1]=0; low[r2]=0; ins[r1]=0; ins[r2]=0;
					par[r1]=0; par[r2]=0;
					q[++size]=r1; q[++size]=r2; 
				}
			}
		    for (int k=i;k<=j;k++)
		     if (pd[a[k].num])
		      {
		      	int r1=find(a[k].x); int r2=find(a[k].y);
		      	add(r1,r2,a[k].num);
		      	//cout<<r1<<" "<<r2<<endl;
		      }
		    for (int k=1;k<=size;k++)
		     if (!dfsn[q[k]])  tarjan(q[k]);
		    for (int k=i;k<=j;k++)
		    {
		    	int r1=find(a[k].x);
		    	int r2=find(a[k].y);
		    	if (r1!=r2)
		    	 {
		    	 	num++;
		    	 	fa[r2]=r1;
		    	 }
		    }
		    if (num==n-1) break;
		    i=j+1;
		}
	}
	for (int i=1;i<=m;i++)
	 if (!pd[i]) printf("none\n");
	 else if (pd[i]==1) printf("at least one\n");
	 else printf("any\n");
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值